ViewPager stucks while swiping views - android

I have created a viewpager with tabslayout.Each view of viewpager consists of recyclerview inside fragment.The issue i am having is that when i swipe through the views it stucks while swiping in such a manner that there is no change in view and i have to swipe multiple times in order to change view.It looks that swipe event is not detected by viewpager.However if i comment out below two lines which includes setting adapter then i don't experience such issue and swipe is smooth and fast.
PlanListAdapter mAdapter = new PlanListAdapter(browsePlanModelList, getActivity());
rvBrowsePlanList.setAdapter(mAdapter);
Where am i going wrong and how can i resolve this?
Below is code of fragment and adapter-
public class FullTimeFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_browse_plan_list, container, false);
ArrayList<BrowsePlanModel> browsePlanModelList = (ArrayList<BrowsePlanModel>) getArguments().getSerializable(AppConstant.BROWSEPLANlIST);
RecyclerView rvBrowsePlanList = (RecyclerView) view.findViewById(R.id.rvBrowsePlanList);
//rvBrowsePlanList.addItemDecoration(new SimpleDividerIncludingLastItemDecoration(getResources()));
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
rvBrowsePlanList.setLayoutManager(mLayoutManager);
rvBrowsePlanList.setItemAnimator(new DefaultItemAnimator());
if(browsePlanModelList!=null)
{
BrowsePlanListAdapter mAdapter = new BrowsePlanListAdapter(browsePlanModelList, getActivity());
rvBrowsePlanList.setAdapter(mAdapter);
}
return view;
}
}
Adapter
public class PlanListAdapter extends RecyclerView.Adapter<PlanListAdapter.MyViewHolder> {
private List<BrowsePlanModel> planList;
Context mContext;
public PlanListAdapter(List<BrowsePlanModel> planList, Context mContext) {
this.mContext = mContext;
this.planList = planList;
}
#Override
public PlanListAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sffs_item, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(PlanListAdapter.MyViewHolder holder, int position) {
holder.xyz.setText(planList.get(position).getxyx());
holder.abc.setText(planList.get(position).getabc());
}
#Override
public int getItemCount() {
return planList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView xyz, abc;
public MyViewHolder(View itemView) {
super(itemView);
xyz = (TextView) itemView.findViewById(R.id.xyz);
abc = (TextView) itemView.findViewById(R.id.abc);
}
}
}

Use
myViewPager.setOffscreenPageLimit(number of pages in your viewpager minus 1);
If you do not do this, the pages which are not currently visible will be destroyed and will be recreated every time you swipe. This will lead to huge lag if each of those pages is populating a RecyclerView from a database, etc.

It may be happening because of high resolution image. If you use high resolution images then the viewPager will stuck while swiping . Try using low resolution images.

Related

Android: Recyclerview in Bottom Navigation Bar Fragment populated with SQLite will not update

I have a BottomNavigationBar with 3 fragments. In the first fragment, I try to put SQLite data into a recyclerview. It works fine except for the fact that I need to switch between the Navigation Bar items in order to see the refreshed recyclerview. When I use a handler with postDelayed however, it does show the refreshed recyclerview if I set around 1 sec of delay. 0.2 secs wont work already.
Even though this is still very generic: is there any best practice for this? It seems to me that I need to use AsyncTask which has been -however- deprecated.
Thanks!
Simon
HomeFragment
public class HomeFragment extends Fragment {
private HomeViewModel homeViewModel;
private Context context;
private CardView cardview;
private LinearLayout.LayoutParams layoutparams;
private TextView textview;
private RelativeLayout relativeLayout;
private myDbAdapter helper;
RecyclerView myView;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
View root = inflater.inflate(R.layout.fragment_home, container, false);
helper = new myDbAdapter(getContext());
myView = (RecyclerView) root.findViewById(R.id.recyclerview_home);
RecyclerViewAdapter3 adapter = new RecyclerViewAdapter3(new ArrayList<String>(Arrays.asList(helper.classes())));
myView.setHasFixedSize(true);
myView.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
myView.setLayoutManager(llm);
homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(#Nullable String s) {
textView.setText(s);
}
});
return root;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
public void refresh(View v){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
myView = (RecyclerView) v.findViewById(R.id.recyclerview_home);
helper = new myDbAdapter(v.getContext());
ArrayList<String> classes = new ArrayList(Arrays.asList(helper.classes()));
ArrayList<String> subClasses = new ArrayList(Arrays.asList(helper.subClasses()));
RecyclerViewAdapter3 adapter = new RecyclerViewAdapter3(classes);
myView.setHasFixedSize(true);
myView.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(v.getContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
myView.setLayoutManager(llm);
}
}, 1000); //time in millis
}
}
RecyclerViewAdapter3
public class RecyclerViewAdapter3 extends RecyclerView.Adapter<RecyclerViewAdapter3.MyViewHolder> {
public ArrayList<String> classArrayList;
public ArrayList<String> subClassArrayList;
myDbAdapter helper;
public RecyclerViewAdapter3(ArrayList<String> classArrayList){
this.classArrayList = classArrayList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View listItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview, parent, false);
return new MyViewHolder(listItem);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.class.setText(classArrayList.get(position));
holder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
helper = new myDbAdapter(v.getContext());
helper.delete(classArrayList.get(position));
HomeFragment homeFragment = new HomeFragment();
homeFragment.refresh(v.getRootView());
}
});
holder.selectButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}});}
#Override
public int getItemCount() {
return classArrayList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
private TextView class;
private Button selectButton;
private ImageView delete;
public MyViewHolder(View itemView) {
super(itemView);
class = (TextView)itemView.findViewById(R.id.name);
selectButton = (Button) itemView.findViewById(R.id.selectButton);
delete = (ImageView) itemView.findViewById(R.id.delete);
}
}
}
Thanks for posting your code :)
There are a fair few things that can go wrong in your code as it is right now, and I can't really pinpoint what causes it to work when you use postDelay. I'm going to list a few, which you can look into:
From your onClick() inside your ViewHolder
HomeFragment homeFragment = new HomeFragment();
homeFragment.refresh(v.getRootView());
You should really not instantiate your fragments like this. You can instead pass a callback from your fragment to your adapter (eg.: View.OnClickListener)
You keep re-instantiating your adapter and your helper needlessly. You should create your adapter only once, set it as your recycler view adapter, and save it in a member variable.
Proposed solution
I see that you're already using ViewModel, so you're on a great path for a less error-prone screen, so I suggest that you move your db query-ing logic to your view model. If you're using raw SQLite (instead of Room), you can extend AndroidViewModel, so you'll have access to a context right away. And as you do with your homeViewModel.getText(), you should expose the classes array as live data, observe it, and submit the new list to your adapter.
For submitting your list to your adapter I suggest using ListAdapter, this will provide you a submitList method for submitting the list in the fragment, and inside the adapter, you will have a getItem(int position) method, which you can query inside the onBindViewHolder method.
Inside your fragment, it'll look something like this:
ClassAdapter adapter = null;
View onCreateView(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
View root = inflater.inflate(R.layout.fragment_home, container, false);
adapter = new ClassAdapter(
new ClassDeleteCallback() {
#Override
void onClassDeleted(Class class) {
// inside view model we can modify db state, than refresh live data
viewModel.deleteClass(class);
}
},
new ClassSelectedCallback() {
// follows same pattern of above
}
);
RecyclerView rv = root.findViewById(R.id.my_rv);
rv.setAdapter(adapter);
rv.setLayoutManager(new LinearLayoutManager(getContext());
homeViewModel.getClasses().observe(getViewLifecycleOwner(), new Observer<List<Class>>() {
#Override
public void onChanged(#Nullable List<Class> classes) {
adapter.submitList(classes);
}
});
homeViewModel.refreshClasses();
return root;
}
I can highly recommend for you to study this project a bit, because it covers lot of the basics which can lead to a much stabler app: Sunflower sample app
I think you should read a bit more about the architecture components, and then go through some code-labs and stuff, and have another go with this screen starting from square one, because it will be easier than fixing the current state :)
I hope this was helpful, and not too discouraging!

How to replace a fragment with another fragment with a web view to open different URLs by clicking on different items in the recyclerview?

My problem is, I have a recyclerview implemented in a fragment called "FragmentNewsItems" that lists out the set of news (images + text) in cardview. So my task is, to make the app, so that when I click on one of those items in the recyclerview, the app would replace it with another fragment with a web view.
Some good people advised me to create a click event:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
// add this line
private AdapterView.OnItemClickListener onItemClickListener;
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView image;
private TextView text;
FragmentNewsItems fragmentNewsItems = new FragmentNewsItems();
public ViewHolder(#NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
text = itemView.findViewById(R.id.text);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
mAdapter.onItemHolderClick(ViewHolder.this);
}
}
public void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
private void onItemHolderClick(ViewHolder holder) {
if (onItemClickListener != null)
onItemClickListener.onItemClick(null, holder.itemView, holder.getAdapterPosition(), holder.getItemId());
}
}
and then add the click event to the fragment:
public class FragmentNewsItems extends Fragment implements AdapterView.OnItemClickListener{
View view;
private RecyclerView recyclerView;
private List<NewsItems> cyberNews;
private FragmentNewsItems fragmentNewsItems;
public FragmentNewsItems(){
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.f_news_items_layout, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.news_items_recycler_view);
RecyclerViewAdapter recyclerAdapter = new RecyclerViewAdapter(getContext(), cyberNews);
// add this line
recyclerAdapter .setOnItemClickListener(this);
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
recyclerView.setAdapter(recyclerAdapter);
return view;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
cyberNews = new ArrayList<>();
cyberNews.add(new NewsItems(R.drawable.cyber1, "The of cybersecurity in 5G-connected world"));
cyberNews.add(new NewsItems(R.drawable.cyber2, "Google discovered several iPhone security flaws, and Apple still hasn't patched one"));
cyberNews.add(new NewsItems(R.drawable.cyber3, "WhatsApp and Telegram media files aren't so secure"));
cyberNews.add(new NewsItems(R.drawable.cyber4, "Microsoft Exposes Russian Cyberattacks on Phones, Printers, Video Decoders"));
cyberNews.add(new NewsItems(R.drawable.cyber5, "Tesla demonstrated the power of The Internet of Things"));
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// here open new activity or replace fragment which have webview.
}
Like this:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// here open new activity or replace fragment which have webview.
}
But you see a comment which says open a new activity or replace fragment which have a webview. I tried multiple times to replace with another fragment, but it simply does not open my fragment. I mean, it loads the layout, but does not replace with another fragment. I am a new to Android, and to be honest I have no idea what's wrong. Can you help me with coding?
create a new fragment for web view and pass your data and replace new fragment using your activity:
check my answer for this question:
https://stackoverflow.com/questions/58680300

open a Fragment by clicking on a recyclerview item and move to this fragment

i'm trying to open a Fragment by clicking on a recyclerview item, what i'm getting after clicking on item is both fragment toghter, i see the image from the fragment that i want to go to after the clicking on my recyclerview..Does anyone have any idea what i'm doing wrong?
public class MainActivity extends AppCompatActivity {
private FragmentManager manger;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<Chat> chatList = new ArrayList<>();
chatList.add(new Chat("send post card", " ", R.drawable.sentpostcard));
chatList.add(new Chat("send greeting card", "happy holiday", R.drawable.greetingcard));
chatList.add(new Chat("special designs", "choose a card", R.drawable.special));
ChatAdapter adapter = new ChatAdapter(chatList);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.chatList);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
// 1
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder>{
// 7
private ArrayList<Chat> chats;
// 8
public ChatAdapter(ArrayList<Chat> chats) {
this.chats = chats;
}
// 10
#Override
public ChatViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = getLayoutInflater();
View v = inflater.inflate(R.layout.chat_list_item, parent, false); // false == do not attach to root
return new ChatViewHolder(v);
}
// 11
#Override
public void onBindViewHolder(ChatViewHolder holder, int position) {
holder.bind( chats.get(position) );
}
// 9
#Override
public int getItemCount() {
return chats.size();
}
// 2
public class ChatViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
//4
private TextView textName, textChat;
private ImageView imageChat;
//we need to remember a chat for each view holder and we bind the chat object using the bind function
//this will be useful later in the onClick Listener
private Chat c;
// view is the layout view ==> it contains all of the view in the layout file
// 3
public ChatViewHolder(View itemView) {
super(itemView);
// 5
textName = (TextView) itemView.findViewById(R.id.textName);
textChat = (TextView) itemView.findViewById(R.id.textChat);
imageChat = (ImageView) itemView.findViewById(R.id.imageChat);
// 12
itemView.setOnClickListener(this);
}
// 6
public void bind(Chat chat){
c = chat;
textName.setText(chat.getName());
textChat.setText(chat.getText());
imageChat.setImageResource(chat.getImage());
}
#Override
public void onClick(View v) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new FragA()).commit();
}
}
}
}
Would be nice to see you layout xml, but from what I can see:
Your recycler view is not inside a fragment - it is inside the MainActivity. You set it with setContentView(R.layout.activity_main);
When you do
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new FragA()).commit();
you put your new fragment in the fragment_container view group. But this does not affect the activity at all. So, you end up with both views visible.
There are two ways to fix this:
The easy and hacky one - set solid background (i.e android:background="#android:color/black") to your fragment and add android:clickable="true" to it's root layout. Then you won't be able to see or interact with the activity that will be beneath your fragment.
More proper way - put your recycler view in a fragment, put that fragment in the fragment_container with getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, recyclerFragment).commit();. Then it will be replaced with your new fragment on item click when you call getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new FragA()).commit();
To be able to go back to the recycler view you will also need to call addToBackStack method

Unable to use recycler views in multiple tabs

I have three tabs.For testing purposes,I am setting the same fragment to all the three tabs.Fragment has a recycler view. I am able to view the recycler view only in first fragment.
Activity Code:
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new TrialsFragment(), getString(R.string.camps));
adapter.addFragment(new TrialsFragment(), getString(R.string.trials));
adapter.addFragment(new TrialsFragment(), getString(R.string.events));
viewPager.setAdapter(adapter);
Fragment Code:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
recyclerView = (RecyclerView) getActivity().findViewById(R.id.recycler);
adapter = new TrialsRecyclerViewAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(adapter);
}
Adapter code:
public class TrialsRecyclerViewAdapter extends RecyclerView.Adapter<TrialsRecyclerViewAdapter.ViewHolder> {
String[] names = {"Sample Name","Sample Name","Sample Name","Sample Name","Sample Name"};
String[] regNos ={"12345678","12345678","12345678","12345678","12345678"};
#Override
public TrialsRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CardView cv = (CardView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.trial_card, parent, false);
return new ViewHolder(cv);
}
#Override
public void onBindViewHolder(TrialsRecyclerViewAdapter.ViewHolder holder, int position) {
final CardView cardView = holder.cardView;
TextView name = (TextView)cardView.findViewById(R.id.trial_name);
TextView regNumber = (TextView)cardView.findViewById(R.id.trial_start_end);
name.setText(names[position]);
regNumber.setText(regNos[position]);
cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
#Override
public int getItemCount() {
return names.length;
}
public static class ViewHolder extends RecyclerView.ViewHolder
{
private CardView cardView;
public ViewHolder(CardView v)
{
super(v);
cardView = v;
}
}
}
When the activity is started, RecyclerView is visible in first tab only. If I swipe the tabs, then it becomes visible in second tab only.
I observed the following error while debugging it:
No adapter attached; skipping layout
recyclerView = (RecyclerView) getActivity().findViewById(R.id.recycler);
This is very wrong. Move your logic from onActivityCreated() into onViewCreated(), and get the fragment's own RecyclerView. Each fragment should be creating its own RecyclerView in onCreateView(), which will be the contents of that page of the ViewPager.

Listview header laggy until scrolled

The header for my listview is pretty complex. It is a ViewPager / FragmentStatePagerAdapter with 5 fragments. The strange thing is that when the view first loads the whole listview is laggy. Once you scroll past the header the listview becomes smooth even when you scroll back up to the header. Any ideas on what may be happening? Thanks for any information
Fragment That contains ListView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View featuredView = inflater.inflate(R.layout.fragment_featured_series, container, false);
ButterKnife.inject(this, featuredView);
View headerView = getHeaderView()
featuredList.addHeaderView(featuredListHeaderView);
sAdapter = new SeriesAdapter(getActivity(), new ArrayList<SeriesItem>());
featuredList.setAdapter(sAdapter);
return featuredView;
}
RelativeLayout getHeaderView() {
featuredListHeaderView = new FeaturedListHeaderView(getActivity(), this, rHeight, recentHeight);
featuredListHeaderView.setLayoutParams(params);
featuredListHeaderView.setVisibility(View.INVISIBLE);
return featuredListHeaderView;
}
}
The Header contains a Fragment which contains the pager
...
pagerAdapter = new FeaturedPagerAdapter(getActivity().getSupportFragmentManager(), new ArrayList<SeriesItem>());
viewPager.setAdapter(pagerAdapter);
pageIndicator.setViewPager(viewPager);
private class FeaturedPagerAdapter extends FragmentStatePagerAdapter {
private List<SeriesItem> seriesItems = new ArrayList<SeriesItem>();
public FeaturedPagerAdapter(FragmentManager fm, List<SeriesItem> sItems) {
super(fm);
this.seriesItems = sItems;
}
#Override
public Fragment getItem(int i) {
FeaturedPageFragment pf = new FeaturedPageFragment(seriesItems.get(i));
return pf;
}
#Override
public int getCount() {
return seriesItems.size();
}
#Override
public CharSequence getPageTitle(int position) {
return seriesItems.get(position).getName();
}
public void setItems(List<SeriesItem> items) {
this.seriesItems = items;
notifyDataSetChanged();
pageIndicator.notifyDataSetChanged();
}
}
FeaturedPageFragment just contains a image with some click listeners

Categories

Resources