简体   繁体   中英

How can you change ViewPager2 position inside the ViewPager2Adapter?

I programmed a Vocabulary Trainer with Vocabulary Cards. The Vocabulary Cards are Entries in a Room Database created from an asset. I am displaying these Vocabulary Cards with ViewPager2 in an Activity. I have a 'correct' and a 'false' button and when the user clicks on either, I want to update the Vocabulary Card (-> The entry in the sqlite database) and automatically swipe to the next item of the ViewPager2.

If I implement the buttons in the ViewPager2Adapter, I can't find a way to change the position of the ViewPager2. If I implement the buttons in the activity the sqlite entry does not update properly (After it updates the entry, the activity is constantly refreshed, it seems like it never the leaves the OnClick methode of the button).

So is it possible to change the position of ViewPager2 from inside the ViewPager2Adpater?

Thanks for your help!

That is the relevant code if I have the buttons in my ViewPager2Adapter. Here I don't know how to change the position of the ViewPager2

public void onBindViewHolder(@NonNull @NotNull ViewHolder holder, int position) {
    VocabularyCard vocabularyCard = currentCards.get(position);
    holder.btn_correct.setOnClickListener(view -> {
        vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
    });
    holder.btn_false.setOnClickListener(v15 -> {
        vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
    });


That is the relevant code if I have the buttons in the Activity. Here the update function triggers an infinite updating of the Activity:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);
    initAll();

    btn_correct_2.setOnClickListener(view -> {
        int currentPos = viewpager2.getCurrentItem();
        vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
            if (vocabularyCards.size() == currentPos){
                Intent intent = new Intent(TestActivity.this, MainActivity.class);
                startActivity(intent);
            }else {
                viewpager2.setCurrentItem(currentPos + 1);
            }
            VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
            vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
        });

    });
    btn_false_2.setOnClickListener(view -> {
        int currentPos = viewpager2.getCurrentItem();
        vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
            if (vocabularyCards.size() == currentPos){
                Intent intent = new Intent(TestActivity.this, MainActivity.class);
                startActivity(intent);
            }else {
                viewpager2.setCurrentItem(currentPos + 1);
            }
            VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
            vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
        });

    });




   
        Objects.requireNonNull(getSupportActionBar()).setTitle(getResources().getString(R.string.learn_new_words));
        LiveData<List<VocabularyCard>> allNewCards = vocabularyViewModel.getAllNewCards(goal);
        allNewCards.observe(this, vocabularyCards -> vocabularyViewModel.setCurrentCards(vocabularyCards));
        vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
            viewPager2Adapter.setCurrentCards(vocabularyCards);
            viewpager2.setAdapter(viewPager2Adapter);
            viewpager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    super.onPageScrolled(position, positionOffset, positionOffsetPixels);
                }

                @Override
                public void onPageSelected(int position) {
                    super.onPageSelected(position);
                }

                @Override
                public void onPageScrollStateChanged(int state) {
                    super.onPageScrollStateChanged(state);
                }
            });
        });
   

The update function in the Room DAO is straightforward:

@Update
void updateSingleVocabularyCard(VocabularyCard vocabularyCard);

I left out all the code that is not relevant.

There are several ways to propagate an event from the adapter to the activity where you manage your cards using ViewPager2 . Let's have a look how it can be done either using an interface or using the same view model. But in any case I strongly recommend you to update your database in a background thread to prevent any possible UI lags.

1. Using an interface

This option is more flexible since you can propagate events as well as pass data as parameters. You can also reuse this interface for other cases. As far as I See you have a holder that has 2 buttons for the users to make choices. So our event here would be something like ChoiceEventListener , let's call this interface like so. Then you'd have to add a method to handle this event from within anywhere you wanna hear this event, and let's call its handle method onChoice() . Finally we would need a variable to indicate what the choice is. Now that ready to implement, let's write the new interface...

ChoiceEventListener.java

public interface ChoiceEventListener {
    void onChoice(VocabularyCard vocabularyCard, boolean choice);
}

The next thing to do is to implement this interface where you want to listen to this event. In this case it is in your activity. There are 2 ways to do this:

  1. You make your activity to inherit its methods using the implements keyword
    YourActivity.java
public class YourActivity extends AppCompatActivity implements ChoiceEventListener {

    // Use a background thread for database operations
    private Executor executor = Executors.newSingleThreadExecutor();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        initAll();

        // You must construct your adapter class with the listener
        ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, this);
    }

    @Override
    public void onChoice(VocabularyCard vocabularyCard, boolean choice) {
        if(choice) {
            // User pressed the correct button
        }
        else {
            // User pressed the false button
        }

        // Update card in the background
        executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
    }
}
  1. You can implement it as an anonymous function
    YourActivity.java
public class YourActivity extends AppCompatActivity {

    // Use a background thread for database operations
    private Executor executor = Executors.newSingleThreadExecutor();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        initAll();

        // You must construct your adapter class with the listener
        ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, (vocabularyCard, choice) -> {
            if(choice) {
                // User pressed the correct button
            }
            else {
                // User pressed the false button
            }

            // Update card in the background
            executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
        });
    }
}

Finally the ViewPager2Adapter class implementation would be something like this: ViewPager2Adapter.java

public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {

    // Here is your listener to deliver the choice event to it
    private final ChoiceEventListener listener;

    // Constructor
    public ViewPager2Adapter(/* Other params... */, ChoiceEventListener listener) {
        /* Other inits */
        this.listener = listener;
    }


    public void onBindViewHolder(@NonNull @NotNull ViewHolder holder, int position) {
        VocabularyCard vocabularyCard = currentCards.get(position);
        holder.btn_correct.setOnClickListener(view -> {
            listener.onChoice(vocabularyCard, true); // true for correct
        });
        holder.btn_false.setOnClickListener(v15 -> {
            listener.onChoice(vocabularyCard, false); // false for false :)
        });
    }
}

2. Use the ViewModel for inter-communication

In this option we use a LiveData object to make page switching. The only thing you need to know in your activity is the current position which you get it from the adapter class. Once you update it in the adapter, set the current position value in live data so that you can switch the page in your activity.

VocabularyViewModel.java

public class VocabularyViewModel extends ViewModel {
    public MutableLiveData<Integer> mldCurrentPosition = new MutableLiveData<>(0);
}

YourActivity.java

public class YourActivity extends AppCompatActivity {

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        initAll();

        vocabularyViewModel.mldCurrentPosition().observe(this, currentPosition -> {
            if(currenPosition == null) return; // ignore when null
            viewpager2.setCurrentItem(currentPosition + 1);
        }
    }
}

Finally the ViewPager2Adapter class implementation would be something like this: ViewPager2Adapter.java

public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {

    // Use a background thread for database operations
    private Executor executor = Executors.newSingleThreadExecutor();

    public void onBindViewHolder(@NonNull @NotNull ViewHolder holder, int position) {
        VocabularyCard vocabularyCard = currentCards.get(position);
        holder.btn_correct.setOnClickListener(view -> {
            // Update card in the background
            executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
            // Then invoke switching to the next card
            vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
        });
        holder.btn_false.setOnClickListener(v15 -> {
            // Update card in the background
            executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
            // Then invoke switching to the next card
            vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
        });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM