simple animation is skipping on older devices - android

I have a RecyclerView with multiple TextViews and a Button. The button triggers an animation like the gif below:
I am doing this by creating a RecyclerView.Adapter and creating an OnClickListener like so:
public class recyclerViewAdapter extends RecyclerView.Adapter<recyclerViewAdapter.ViewHolder> {
private ArrayList<MyObject> objects;
// constructor and other stuff ...
public void setDefaultRequestBtnClickListener(View.OnClickListener defaultRequestBtnClickListener) {
this.defaultRequestBtnClickListener = defaultRequestBtnClickListener;
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
MyObject object = objects.get(position);
if (object.getRequestBtnClickListener() != null) {
holder.Btn.setOnClickListener(object.getRequestBtnClickListener());
}else {
holder.Btn.setOnClickListener(defaultRequestBtnClickListener);
}
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView value;
TextView currentVal;
TextView Btn;
ConstraintLayout parent;
public ViewHolder(#NonNull View itemView) {
super(itemView);
value = itemView.findViewById(R.id.totalPrice);
currentVal = itemView.findViewById(R.id.checkBtn);
Btn = itemView.findViewById(R.id.animateBtn);
parent = itemView.findViewById(R.id.parent);
}
public void bind(final OnMyClickListener listener) {
parent.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onMyClick(v, getLayoutPosition());
}
});
}
}
public interface OnMyClickListener{
void onMyClick(View view, int position);
}
}
this creates and binds a viewholder in a recyclerview to the TextViews and buttons. At the moment a user has to click the element to activate the Btn and animation. The below is the Fragment that is initiating the adapter:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.frag_object_layout, container, false);
final RecyclerView recyclerView = rootView.findViewById(R.id.list);
final ArrayList<MyObject> objects = MyObject.getTestingList();
final recyclerViewAdapter adapter = new recyclerViewAdapter(objects, getContext());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter.setDefaultRequestBtnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
recyclerViewAdapter.ViewHolder holder = (recyclerViewAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(Position);
if (holder != null) {
if (holder.value.getAlpha() == 0.0f) {
holder.value.animate().alpha(1.0f).setDuration(500);
holder.value.animate().translationY(-38).setDuration(1000);
holder.checkValue.animate().translationY(38).setDuration(1000);
} else {
holder.value.animate().alpha(0.0f).setDuration(500);
holder.value.animate().translationY(0).setDuration(500);
holder.checkValue.animate().translationY(0).setDuration(500);
}
}
}
});
adapter.setOnMyClickListener(new recyclerViewAdapter.OnMyClickListener() {
#Override
public void onMyClick(View view, int pos) {
Position = pos;
holder = (recyclerViewAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(Position);
}
});
return rootView;
}
}
This is all working great on an emulator, but on an older device the animation is skipping and the views go directly to their final position. Why is that? I checked the processing time and it is below the 16ms threshold.
How can I improve this?
(Posting the whole code is difficult here. ask for code and I will update the question)

Related

android simple animation is skipping on older device

I have a RecyclerView with multiple TextViews and a Button. The button triggers an animation like the gif below:
I am doing this by creating a RecyclerView.Adapter and creating an OnClickListener like so:
public class recyclerViewAdapter extends RecyclerView.Adapter<recyclerViewAdapter.ViewHolder> {
private ArrayList<MyObject> objects;
// constructor and other stuff ...
public void setDefaultRequestBtnClickListener(View.OnClickListener defaultRequestBtnClickListener) {
this.defaultRequestBtnClickListener = defaultRequestBtnClickListener;
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
MyObject object = objects.get(position);
if (object.getRequestBtnClickListener() != null) {
holder.Btn.setOnClickListener(object.getRequestBtnClickListener());
}else {
holder.Btn.setOnClickListener(defaultRequestBtnClickListener);
}
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView value;
TextView currentVal;
TextView Btn;
ConstraintLayout parent;
public ViewHolder(#NonNull View itemView) {
super(itemView);
value = itemView.findViewById(R.id.totalPrice);
currentVal = itemView.findViewById(R.id.checkBtn);
Btn = itemView.findViewById(R.id.animateBtn);
parent = itemView.findViewById(R.id.parent);
}
public void bind(final OnMyClickListener listener) {
parent.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onMyClick(v, getLayoutPosition());
}
});
}
}
public interface OnMyClickListener{
void onMyClick(View view, int position);
}
}
this creates and binds a view holder in a recycler view to the TextViews and buttons. At the moment a user has to click the element to activate the Btn and animation. The below is the fragment that is initiating the adapter:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.frag_object_layout, container, false);
final RecyclerView recyclerView = rootView.findViewById(R.id.list);
final ArrayList<MyObject> objects = MyObject.getTestingList();
final recyclerViewAdapter adapter = new recyclerViewAdapter(objects, getContext());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter.setDefaultRequestBtnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
recyclerViewAdapter.ViewHolder holder = (recyclerViewAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(Position);
if (holder != null) {
if (holder.value.getAlpha() == 0.0f) {
holder.value.animate().alpha(1.0f).setDuration(500);
holder.value.animate().translationY(-38).setDuration(1000);
holder.checkValue.animate().translationY(38).setDuration(1000);
} else {
holder.value.animate().alpha(0.0f).setDuration(500);
holder.value.animate().translationY(0).setDuration(500);
holder.checkValue.animate().translationY(0).setDuration(500);
}
}
}
});
adapter.setOnMyClickListener(new recyclerViewAdapter.OnMyClickListener() {
#Override
public void onMyClick(View view, int pos) {
Position = pos;
holder = (recyclerViewAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(Position);
}
});
return rootView;
}
}
This is all working great on an emulator, but on an older device the animation is skipping and the views go directly to their final position. Why is that? I checked the processing time and it is below the 16ms threshold.
How can I improve this?
(Posting the whole code is difficult here. ask for code and I will update the question)
The issue here is that I was doing this programmatically. For some reason, if I call Animator object and pass anim xml files then the animation works (on old device). Does anyone know why?

ImageView in RecyclerView

In my MainActivity.java I have a sort of button to login. I also have 4 fragments, each of them containing a recycler view (imageview + textview).
The imageview is hidden, but when I press the button it becomes visibile. How can I do that? The notifyDataSetChanged() doesn't work in the adapter and I can't call it on the adapter in the fragment page, because is outside the onCreateView method.
Here's my code:
MainActivity: contains the button
Fragment Page:
public class Classici_Tab extends Fragment {
LinkedList<Book> catalog = new LinkedList<>();
RecyclerView mRecyclerView;
BookListAdapter mAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.classici_tab, container, false);
fillCatalog("catalog.csv"); //fill the LinkedList
mRecyclerView = (RecyclerView) fragmentView.findViewById(R.id.classici_rv);
mAdapter = new BookListAdapter(getContext(), catalog);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
return fragmentView;
}
}
Adapter:
public class BookListAdapter extends RecyclerView.Adapter<BookListAdapter.BookViewHolder> {
// View Holder
class BookViewHolder extends RecyclerView.ViewHolder
{
public final ImageView cover;
public final TextView title;
public final TextView author;
public final ImageView availability;
final BookListAdapter mAdapter;
public BookViewHolder(View itemView, BookListAdapter adapter) {
super(itemView);
cover = (ImageView) itemView.findViewById(R.id.cover);
title = (TextView) itemView.findViewById(R.id.title);
author = (TextView) itemView.findViewById(R.id.author);
availability = (ImageView) itemView.findViewById(R.id.availability);
MainActivity m = new MainActivity();
if(m.loggedin) availability.setVisibility(View.VISIBLE);
else availability.setVisibility(View.INVISIBLE);
this.mAdapter = adapter;
}
}
private LinkedList<Book> mBookList = new LinkedList<>();
private LayoutInflater mInflater;
boolean isVisible = false;
public BookListAdapter() {}
public BookListAdapter(Context context, LinkedList<Book> bookList) {
mInflater = LayoutInflater.from(context);
this.mBookList = bookList;
}
//IT DOESN'T WORK
public void setPicturesVisibility(boolean isVisible){
this.isVisible = true;
notifyDataSetChanged();
}
#Override
public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mItemView = mInflater.inflate(R.layout.tab_list_item, parent, false);
return new BookViewHolder(mItemView, this);
}
#Override
public void onBindViewHolder(BookViewHolder holder, int position) {
holder.cover.setImageResource(mBookList.get(position).getCover());
holder.title.setText(mBookList.get(position).getTitle());
holder.author.setText(mBookList.get(position).getAuthor());
holder.availability.setImageResource(mBookList.get(position).getAvailability());
if(isVisible){
holder.availability.setVisibility(View.VISIBLE);
}else{
holder.availability.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return mBookList.size();
}
}
Not sure why you need to recreate adapter. Without recreating adapter, you can add listener to the button and in the onclick event handler, make image visible in recyclerview adapter's viewholder constructor.
public ViewHolder(View view) {
super(view);
textview = view.findViewById(R.id.textview);
imgView = view.findViewById(R.id.storeImg);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imgView.setVisibility(View.VISIBLE);
}
});
}
You can find recyclerview examples here http://www.zoftino.com/android
There is no need to recreate adapter. You can add a boolean arePicturesVisible = false; in your RecyclerViewAdapter.
And make a method in adapter:
public void setPicturesVisibility(boolean areVisible){
arePicturesVisible = areVisible;
notifyDataSetChanged();
}
Then add:
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
.....
if(arePicturesVisible){
holder.image.setVisibility(View.VISIBLE);
}else{
holder.image.setVisibility(View.INVISIBLE);
}
.....
}
Then in your button onClick() method call setPicturesVisibility() from RecyclerView adapter.

Removing recyclerview items from the Adapter

I have two tabs (fragments), NewOrders and FinishedOrders, I'm populating the orders via Volley requests, now each item inside the New Orders tab has a SwipeLayout which show a clickable textview that makes the order finished, and move it to the other tab (backend stuff..), and I got this working perfectly,
The problem is when I click to finish, the recyclerview isn't updated once the request sent successfully, I have to do pull-to-refresh so it would update..! it seems easy to solve, but the issue is handling the swipelayout listener done inside onBindView method inside the adapter..!! that's only place to access it according to the library I'm using (I guess)..! on the other hand refreshing and populating the list happens in the NewOrder tab fragment..!
So how can I make the item to be removed from the list after the click and becomes updated..?!
Any thoughts..!?
My Adapter Class + ViewHolder
Note: the implemented methods in the adapter are required because of the interface of SwipeLayout library
public class OrdersDataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
implements SwipeAdapterInterface, SwipeItemMangerInterface {
protected SwipeItemRecyclerMangerImpl mItemManger = new SwipeItemRecyclerMangerImpl(this);
public Context context;
ArrayList<OrderPresenter> orders;
public OrdersDataAdapter(ArrayList<OrderPresenter> orders, Context context) {
this.orders = orders;
this.context = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.order_card, parent, false);
return new NewOrderVH(v);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
final OrderPresenter order = this.orders.get(position);
final NewOrderVH vh1 = (NewOrderVH) holder;
vh1.setData(orders.get(position));
mItemManger.bindView(vh1.itemView, position);
vh1.swipeLayout.setShowMode(SwipeLayout.ShowMode.PullOut);
vh1.swipeLayout.addDrag(SwipeLayout.DragEdge.Left,
vh1.swipeLayout.findViewById(R.id.bottom_wrapper));
if (order.isFinished()) {
vh1.swipeLayout.setSwipeEnabled(false);
vh1.setBadge("DONE");
vh1.setBadgeColor(order.getBadgeColor());
} else {
vh1.finish.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// get the clicked item position?
final int position = vh1.getAdapterPosition();
// these responsible for the request which make the order finished
OrderPresenter order = orders.get(position);
OrderRepository.setOrderFinURL(order.getID());
OrderRepository.FinishOrder(order.getID(), context);
/*the commented three lines below didn't help with the problem*/
// notifyItemChanged(position);
// notifyItemRemoved(position);
// notifyDataSetChanged();*/
order.setStatus(order.getStatusText(Order.FINISHED));
}
});
}
}
#Override
public int getItemCount() {
return orders.size();
}
public class NewOrderVH extends RecyclerView.ViewHolder {
SwipeLayout swipeLayout;
private TextView finish;
private CardView orderCard;
TextView Badge;
private ImageView cusPic;
private TextView cusName;
private TextView CusAdress;
private TextView vendorsNum;
private TextView itemsNum;
private TextView time;
private TextView emptyView;
public NewOrderVH(View itemView) {
super(itemView);
Badge = (TextView) itemView.findViewById(R.id.badge);
swipeLayout = (SwipeLayout) itemView.findViewById(R.id.swipe);
finish = (TextView) itemView.findViewById(R.id.finish);
orderCard = (CardView) itemView.findViewById(R.id.OrderCard);
cusPic = (ImageView) itemView.findViewById(R.id.cusPic);
cusName = (TextView) itemView.findViewById(R.id.cusName);
CusAdress = (TextView) itemView.findViewById(R.id.CusAdress);
vendorsNum = (TextView) itemView.findViewById(R.id.vendorsNum);
itemsNum = (TextView) itemView.findViewById(R.id.itemsNum);
time = (TextView) itemView.findViewById(R.id.time);
emptyView = (TextView) itemView.findViewById(R.id.empty_view);
orderCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), OrderDetails.class);
v.getContext().startActivity(intent);
}
});
}
public void setData(final OrderPresenter data) {
time.setText(data.getOrderTime());
cusName.setText(data.getFullName());
vendorsNum.setText(data.getVendorsCount());
itemsNum.setText(data.getItemsCount());
CusAdress.setText(data.getFullAddress());
Picasso.with(context).load(data.getCustomerPicture()).into(cusPic);
}
public void setBadgeColor(int drawable) {
this.Badge.setBackgroundResource(drawable);
}
public void setBadge(String badge) {
this.Badge.setText(badge);
}
}
#Override
public int getSwipeLayoutResourceId(int position) {
return R.id.swipe;
}
#Override
public void openItem(int position) {
}
#Override
public void closeItem(int position) {
}
#Override
public void closeAllExcept(SwipeLayout layout) {
}
#Override
public void closeAllItems() {
}
#Override
public List<Integer> getOpenItems() {
return null;
}
#Override
public List<SwipeLayout> getOpenLayouts() {
return null;
}
#Override
public void removeShownLayouts(SwipeLayout layout) {
}
#Override
public boolean isOpen(int position) {
return false;
}
#Override
public Attributes.Mode getMode() {
return null;
}
#Override
public void setMode(Attributes.Mode mode) {
}
}
My NewOrder Fragment
Note: the FinishedOrders tab (fragment) does the same thing as new order but filters the current the Finished status.
public class NewOrdersTab extends Fragment {
RecyclerView recyclerView;
OrdersDataAdapter adapter;
private SwipeRefreshLayout swiperefresh;
private TextView emptyView;
ArrayList<OrderPresenter> modelData;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.new_orders_tab_frag, container, false);
modelData = new ArrayList<>();
recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
swiperefresh = (SwipeRefreshLayout) rootView.findViewById(R.id.swiperefresh);
recyclerView.setLayoutManager(new LinearLayoutManager(this.getActivity()));
swiperefresh.setColorSchemeResources(R.color.colorPrimary, R.color.color_error, R.color.colorInfo);
adapter = new OrdersDataAdapter(modelData, getActivity());
emptyView = (TextView) rootView.findViewById(R.id.empty_view);
recyclerView.setAdapter(adapter);
adapter.setMode(Attributes.Mode.Single);
OrderRepository.fetchOrders("awaiting-shipment", getActivity(), new DataFetch() {
#Override
public void onResponse(ArrayList<OrderPresenter> data) {
swiperefresh.setRefreshing(true);
if (data.size() != 0) {
swiperefresh.setRefreshing(true);
emptyView.setVisibility(View.GONE);
modelData.clear();
modelData.addAll(data);
adapter.notifyDataSetChanged();
} else {
emptyView.setVisibility(View.VISIBLE);
emptyView.setText(getString(R.string.No_New_Orders));
}
swiperefresh.setRefreshing(false);
}
});
return rootView;
}
}
I figured it out, I just added these two lines after I make the request..!
orders.remove(position);
notifyItemRemoved(position);
//notifyDataSetChanged(position);

How to force RecyclerView.Adapter to call the onBindViewHolder() on all elements

I have a VerticalGridView that is using a RecyclerView.Adapter to populate the elements. I have discovered that the onBindViewHolder() method does not get called if the potential element is off of the viewport. Unfortunately, this is causing a NullPointerException from a different method because I am catching a TextView reference in the onBindViewHolder() method and passing it to an outside variable for later manipulation.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.txtCategoryName.setText(categories.get(position).getStrCategory());
categories.get(position).setTxtViewReference(viewHolder.txtCategoryDefectTotal);
viewHolder.categoryBoxRoot.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for(CategoryListItem catItem : categories){
if(catItem.getStrCategory().equals(viewHolder.txtCategoryName.getText())){
int index = Defects.getInstance().getCategories().indexOf(catItem) + 1;
MainInterface.grids.get(index).bringToFront();
MainInterface.grids.get(index).setVisibility(View.VISIBLE);
for(VerticalGridView grid : MainInterface.grids){
int gridIndex = MainInterface.grids.indexOf(grid);
if(gridIndex != index){
grid.setVisibility(View.INVISIBLE);
}
}
break;
}
}
}
});
From what I understand, the reference to the TextView gets created when the Viewholder object is instantiated.
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtCategoryName;
public TextView txtCategoryDefectTotal;
public View categoryBoxRoot;
public ViewHolder(View itemView) {
super(itemView);
txtCategoryName = (TextView) itemView.findViewById(R.id.textViewCategoryName);
txtCategoryDefectTotal = (TextView) itemView.findViewById(R.id.textViewCategoryTotalDefects);
categoryBoxRoot = itemView.findViewById(R.id.root_category_box);
}
}
Is there a way to force onBindViewHolder() to be called on all elements at least one time when the Adapter is instantiated?
I attempted the suggestions here without any success.
I understand that forcing onBindViewHolder() on all elements would work against the whole purpose of the RecycleView.Adapter. Thus, I am open to any other suggestions on how I can catch that TextView reference.
As a temporary fix to this problem, I am able to use a try catch block around the method that generates the NullPointerException. However, I am concerned that the lack of the reference means I could introduce errors in the future.
The easiest solution for this problem is to scroll down to the bottom of the grid and then back up to the top. This cannot be done in the onCreate() method though because the grid is not technically visible at that time. Instead, it should be called in the onResume() method of the activity.
#Override
public void onResume(){
super.onResume();
VerticalGridView defectGrid = grids.get(0);
RecyclerView.Adapter adapter = defectGrid.getAdapter();
defectGrid.smoothScrollToPosition(adapter.getItemCount()-1);
defectGrid.smoothScrollToPosition(0);
}
This was a good attempt at a solution but unfortunately it still does not work. While it does get the reference to make the method work, it does NOT necessarily have the right reference. I found that the reference can end up pointing at a different TextView as the RecycleView.Adapter reuses views to display them in different areas.
SOLUTION:
Mark Keen was right when he said to use notifyDataSetChanged(). I got it to work by fixing my onBindViewHolder() method to work properly.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.txtCategoryName.setText(categories.get(position).getStrCategory());
viewHolder.txtCategoryDefectTotal.setText(String.valueOf(categories.get(position).getTotalDefectsInCategory()));
}
I also changed my data object so it holds an int value instead of a reference to the TextView since the above proved that reference was invalid. Finally, I added a call to my Adapter when I pressed my custom back button.
grids.get(0).getAdapter().notifyDataSetChanged();
Thank you everyone who contributed!
Just reset the Adapter like this
RecyclerView.Adapter adapter = recList.getAdapter();
recList.setAdapter(null);
recList.setAdapter(adapter);
recList should be you RecyclerView.
wrap your RecyclerView in a NestedScrollView that will force all the items to be bound in advance.
<androidx.core.widget.NestedScrollView
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvGrid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</androidx.core.widget.NestedScrollView>
Understand Flow
public class ThemeGridAdapter extends RecyclerView.Adapter<ThemeGridAdapter.ViewHolder> {
private OnItemClickedListener listener;
private List<Theme> mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public TextView subtitle;
// public SimpleDraweeView image;
public ImageView img;
public ViewGroup container;
public ViewHolder(RelativeLayout v) {
super(v);
container = v;
title = (TextView) v.findViewById(R.id.theme_name_text);
subtitle = (TextView) v.findViewById(R.id.theme_name_type);
// image = (SimpleDraweeView) v.findViewById(R.id.cell_img);
img = (ImageView) v.findViewById(R.id.cell_img);
}
}
public ThemeGridAdapter(List<Theme> dataset) {
mDataset = dataset;
}
#Override
public ThemeGridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RelativeLayout v = (RelativeLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.title.setText(mDataset.get(position).getTitle());
holder.subtitle.setText(mDataset.get(position).getAuthor());
// Uri uri = Uri.parse(mDataset.get(position).getUrl());
holder.img.setImageResource(mDataset.get(position).getImage());
// holder.image.setImageURI(uri);
holder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null)
listener.onItemClicked(holder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return mDataset.size();
}
public void setListener(OnItemClickedListener listener) {
this.listener = listener;
}
}
Check the style of binding view
public class CrimeListFragment extends Fragment {
private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
private static final int REQUEST_CRIME = 1;
private RecyclerView mRecyclerView;
private CrimeAdapter mCrimeAdapter;
private boolean mSubtitleVisible;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
if (savedInstanceState != null){
mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
}
updateUI();
return view;
}
private void updateUI() {
CrimeLab crimeLab = CrimeLab.get(getActivity());
List<Crime> crimes = crimeLab.getCrimes();
if (mCrimeAdapter == null) {
mCrimeAdapter = new CrimeAdapter(crimes);
mRecyclerView.setAdapter(mCrimeAdapter);
} else {
mCrimeAdapter.setCrimes(crimes);
mCrimeAdapter.notifyDataSetChanged();
}
updateSubtitle();
}
private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView mTitleTextView;
private TextView mDateTextView;
private CheckBox mSolvedCheckBox;
private Crime mCrime;
public CrimeHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
mTitleTextView = (TextView) itemView.findViewById(R.id.list_item_crime_title_text_view);
mDateTextView = (TextView) itemView.findViewById(R.id.list_item_crime_date_text_view);
mSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.list_item_crime_solved_checkbox);
}
public void bindCrime(Crime crime) {
mCrime = crime;
mTitleTextView.setText(mCrime.getTitile());
mDateTextView.setText(mCrime.getDate().toString());
mSolvedCheckBox.setChecked(mCrime.isSolved());
}
#Override
public void onClick(View v) {
/*Toast.makeText(getActivity(),mCrime.getTitile() + "clicked!", Toast.LENGTH_SHORT).show();*/
/*Intent for calling activity from fragment*/
/* Intent intent = new Intent(getActivity(), CrimeActivity.class);*/
/*Intent intent = CrimeActivity.newIntent(getActivity(),mCrime.getId());*/
Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
/*startActivityForResult(intent,REQUEST_CRIME);*/
startActivity(intent);
}
}
private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {
private List<Crime> mCrimes;
public CrimeAdapter(List<Crime> crimes) {
mCrimes = crimes;
}
#Override
public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View view = layoutInflater.inflate(R.layout.list_item_crime, parent, false);
return new CrimeHolder(view);
}
#Override
public void onBindViewHolder(CrimeHolder holder, int position) {
Crime crime = mCrimes.get(position);
/*holder.mTextView.setText(crime.getTitile());*/
holder.bindCrime(crime);
}
#Override
public int getItemCount() {
return mCrimes.size();
}
public void setCrimes(List<Crime> crimes) {
mCrimes = crimes;
}
}
#Override
public void onResume() {
super.onResume();
updateUI();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CRIME) {
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
MenuItem subtitleItem = menu.findItem(R.id.menu_item_show_subtitle);
if (mSubtitleVisible){
subtitleItem.setTitle(R.string.hide_subtitle);
}else {
subtitleItem.setTitle(R.string.show_subtitle);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent intent = CrimePagerActivity.newIntent(getActivity(), crime.getId());
startActivity(intent);
return true;
case R.id.menu_item_show_subtitle:
mSubtitleVisible = !mSubtitleVisible;
getActivity().invalidateOptionsMenu();
updateSubtitle();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void updateSubtitle(){
CrimeLab crimeLab = CrimeLab.get(getActivity());
int crimeCount = crimeLab.getCrimes().size();
String subtitle = getString(R.string.subtitle_format,crimeCount);
if (!mSubtitleVisible){
subtitle = null;
}
AppCompatActivity appCompatActivity = (AppCompatActivity)getActivity();
appCompatActivity.getSupportActionBar().setSubtitle(subtitle);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_SUBTITLE_VISIBLE,mSubtitleVisible);
}
}

Send data from CardView in RecyclerView to new fragment?

So I have a CardView in a RecyclerView which has some data inside it. I would like to click on a specific card and launch a new fragment that displays specific data from the data in the CardView (more details about the data in the CardView). Here is the relevant code.
I don't have the most experience in programming and Android in general, any help would be appreciated!
DetailsFragment: `
public class DetailsFragment extends Fragment {
private CardView cardView;
private RecyclerView rv;
private List<Detail> detailList = new ArrayList<>();
public DetailsFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_details, container, false);
Context context = getActivity();
cardView = (CardView) rootView.findViewById(R.id.cv);
rv = (RecyclerView) rootView.findViewById(R.id.rv);
final LinearLayoutManager llm = new LinearLayoutManager(context);
rv.setLayoutManager(llm);
RVDetailAdapter adapter = new RVDetailAdapter(detailList);
rv.setAdapter(adapter);
initializeData();
initializeAdapter();
return rootView;
}
private void initializeData() {
detailList = new ArrayList<>();
// not sure here
detailList.add(new Detail(" "));
}
private void initializeAdapter(){
RVDetailAdapter adapter = new RVDetailAdapter(detailList);
rv.setAdapter(adapter);
}
}
RVNewsAdapater:
public class RVNewsAdapter extends RecyclerView.Adapter<RVNewsAdapter.NewsViewHolder> {
public static final String KEY_LINK ="link";
public static class NewsViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
private static final String TAG = "Hello";
CardView cv;
TextView date;
TextView link;
TextView title;
TextView today;
public NewsViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
date = (TextView) itemView.findViewById(R.id.date);
link = (TextView) itemView.findViewById(R.id.link);
title = (TextView) itemView.findViewById(R.id.heading);
today = (TextView) itemView.findViewById(R.id.today);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Context context = view.getContext();
Intent intent = null;
for(int postion = 0; postion<42; postion++) {
intent = new Intent(context, Details.class);
}
context.startActivity(intent);
}
}
List<News> news;
RVNewsAdapter(List<News> news) {
this.news = news;
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public RVNewsAdapter.NewsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.itemnewsfeed, viewGroup, false);
NewsViewHolder nvh = new NewsViewHolder(v);
return nvh;
}
#Override
public void onBindViewHolder(NewsViewHolder newsViewHolder, int i) {
newsViewHolder.date.setText(news.get(i).date);
newsViewHolder.link.setText(news.get(i).link);
newsViewHolder.title.setText(news.get(i).title);
newsViewHolder.today.setText(news.get(i).today);
}
#Override
public int getItemCount() {
if (news != null) {
return news.size();
}
return 0;
}
}
RVDetailAdapter:
public class RVDetailAdapter extends RecyclerView.Adapter<RVDetailAdapter.DetailViewHolder> {
public static class DetailViewHolder extends RecyclerView.ViewHolder {
TextView link;
public DetailViewHolder(View itemView) {
super(itemView);
link = (TextView) itemView.findViewById(R.id.link);
}
}
List<Detail> detail;
RVDetailAdapter(List<Detail> detail) {
this.detail = detail;
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public RVDetailAdapter.DetailViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.itemnewsfeed, viewGroup, false);
DetailViewHolder dvh = new DetailViewHolder(v);
return dvh;
}
#Override
public void onBindViewHolder(DetailViewHolder detailViewHolder, int i) {
detailViewHolder.link.setText(detail.get(i).link);
}
#Override
public int getItemCount() {
if (detail != null) {
return detail.size();
}
return 0;
}
}
Not sure if you're still looking for an answer, but I just saw the post and thought I'd give you one option.
When I'm using a RecyclerView (doesn't matter if it's using the CardView or not, it's the same for any list item), I create a a click listener for the view item in the Adapter. When the user clicks the item/cardview, I dispatch an item click event. It's your choice on how you respond - you can use a standard callback interface (and pass in your listener when you create the adapter) or use something like EventBus.
Here's an example of the code you would add to your adapter class:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
itemClick(view);
}
});
return new ViewHolder(view);
}
private void itemClick(View view) {
//Handle item event here
//EventBus.getDefault().post(new ItemSelectedEvent(/*send apprpopriate data*/));
}

Categories

Resources