I am programming a simple game in android and I encountered a problem with recyclerview. I need help please , tnx :)
I have an activity that create an arrayList and add my data to it then pass it to my recyclerview's adapter . In the adapter I write code that user can't select more than an image. Now the problem is there when I select first item and then scroll recyclerview I see that one of the last three items is selected. And this case happen for only fist line items and last three items too.
What can I do for that ?
My activity :
public class page_register extends AppCompatActivity {
Activity _A;
Context _C;
private RecyclerView recyclerView1, recyclerView2;
private AdsAdapter adapter1;
private Avatar2Adapter adapter2;
private ArrayList<Ads> adsArrayList;
TextView Tv1, Tv2;
EditText ET1, ET2;
Button BT_First, BT_Second;
ImageButton BT_Back;
ImageView img_first, img_second;
CardView cardView1, cardView2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.page_register);
M_UI.SetOffKeyboard(this);
M_Font.Initial(this);
_A = this;
_C = this;
Initial();
adsArrayList = new ArrayList<>();
adsArrayList.add(new Ads(R.drawable.a1));
adsArrayList.add(new Ads(R.drawable.a2));
adsArrayList.add(new Ads(R.drawable.a3));
adsArrayList.add(new Ads(R.drawable.a4));
adsArrayList.add(new Ads(R.drawable.a5));
adsArrayList.add(new Ads(R.drawable.a6));
adsArrayList.add(new Ads(R.drawable.a7));
adsArrayList.add(new Ads(R.drawable.a8));
adsArrayList.add(new Ads(R.drawable.a9));
adsArrayList.add(new Ads(R.drawable.a10));
adsArrayList.add(new Ads(R.drawable.a11));
adsArrayList.add(new Ads(R.drawable.a12));
adsArrayList.add(new Ads(R.drawable.a13));
adsArrayList.add(new Ads(R.drawable.a14));
adsArrayList.add(new Ads(R.drawable.a15));
adsArrayList.add(new Ads(R.drawable.a16));
adsArrayList.add(new Ads(R.drawable.a17));
adsArrayList.add(new Ads(R.drawable.a18));
adsArrayList.add(new Ads(R.drawable.a19));
adsArrayList.add(new Ads(R.drawable.a20));
adsArrayList.add(new Ads(R.drawable.a21));
//--------------------------------------------
adapter1 = new AdsAdapter(adsArrayList, this, _C, _A, recyclerView1, 0);
adapter2 = new Avatar2Adapter(adsArrayList, this, _C, _A, recyclerView2, 0);
RecyclerView.LayoutManager layoutManager1 = new GridLayoutManager(_C, 3);
RecyclerView.LayoutManager layoutManager2 = new GridLayoutManager(_C, 3);
recyclerView1.setLayoutManager(layoutManager1);
recyclerView2.setLayoutManager(layoutManager2);
recyclerView1.setAdapter(adapter1);
recyclerView2.setAdapter(adapter2);
}
public void Initial() {
recyclerView1 = (RecyclerView) findViewById(R.id.recycler_view1);
recyclerView2 = (RecyclerView) findViewById(R.id.recycler_view2);
}
}
and my adapter :
public class AdsAdapter extends RecyclerView.Adapter<AdsAdapter.AdsViewHolder> {
private ArrayList<Ads> dataList;
Context _C1;
Activity _A1;
RecyclerView r;
int SelectedPos = 0;
int id_prev;
CardView CV_Prev = null;
public AdsAdapter(ArrayList<Ads> dataList, FragmentActivity activity, Context _C, Activity _A, RecyclerView recyclerView, int i) {
this.dataList = dataList;
_A1 = _A;
_C1 = _C;
r = recyclerView;
}
#Override
public AdsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final View view = layoutInflater.inflate(R.layout.avatars, parent, false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View SelectCardView) {
try {
if (CV_Prev != null) {
CV_Prev.findViewById(R.id.img_select).setVisibility(View.INVISIBLE);
}
SelectCardView.findViewById(R.id.img_select).setVisibility(View.VISIBLE);
CV_Prev = (CardView) SelectCardView;
} catch (Exception e) {
Toast.makeText(_C1, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
return new AdsViewHolder(view);
}
#Override
public void onBindViewHolder(AdsViewHolder holder, int position) {
holder.CV_item.setTag(position);
//------------------------------------------------------------------
holder.img_avatar.setImageResource(dataList.get(position).getImage());
}
#Override
public int getItemCount() {
return dataList.size();
}
class AdsViewHolder extends RecyclerView.ViewHolder {
ImageView img_avatar;
CardView CV_item;
AdsViewHolder(View itemView) {
super(itemView);
//-----------------------------------------------------------
img_avatar = (ImageView) itemView.findViewById(R.id.img_avatar);
CV_item = (CardView) itemView.findViewById(R.id.cardView);
}
}
}
my view :
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/cardView"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#00ffffff"
android:layoutDirection="ltr"
android:paddingLeft="5dp"
android:paddingRight="5dp"
card_view:cardBackgroundColor="#B2EBF2"
card_view:cardCornerRadius="5dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/img_avatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00ffffff"
card_view:srcCompat="#drawable/a1" />
<ImageView
android:id="#+id/img_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:visibility="invisible"
card_view:srcCompat="#mipmap/ic_check_circle_black_48dp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
my model :
public class Ads {
private int image;
public Ads(int image)
{
this.image = image;
}
public void setImage(int image) {this.image = image;}
public int getImage() {return image;}
}
below image shows my mean :
*** I tried to set id for each card and image and selecting them by their id but that didn't help too.
This happens because recycler view recycles the view in OnBindViewHolder.To solve this.
create a global variable to store the clicked position.
private mItemSelected=-1;
Then inside viewholder add the clickListener and onClick store the position of the clicked item.
class AdsViewHolder extends RecyclerView.ViewHolder {
ImageView img_avatar;
CardView CV_item;
AdsViewHolder(View itemView) {
super(itemView);
//-----------------------------------------------------------
img_avatar = (ImageView) itemView.findViewById(R.id.img_avatar);
CV_item = (CardView) itemView.findViewById(R.id.cardView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mItemSelected=getAdapterPosition();
notifyDataSetChanged();
}
});
}
}
And in inside OnBindViewHolder,
if(mItemSelected==position){
//code for image selected.
holder.CV_item.setVisibility(View.VISIBLE);
}else{
//code for image unselected.
holder.CV_item.setVisibility(View.INVISIBLE);
}
Also remove the click listener that has been added in the createViewholder instead add it inside the constructor of AdsViewHolder as added above.
EDIT:Check this updated code.Hope it helps you.
public class AdsAdapter extends RecyclerView.Adapter<AdsAdapter.AdsViewHolder> {
private ArrayList<Ads> dataList;
Context _C1;
Activity _A1;
RecyclerView r;
int SelectedPos = 0;
int id_prev;
CardView CV_Prev = null;
private int mItemSelected=-1;
public AdsAdapter(ArrayList<Ads> dataList, FragmentActivity activity, Context _C, Activity _A, RecyclerView recyclerView, int i) {
this.dataList = dataList;
_A1 = _A;
_C1 = _C;
r = recyclerView;
}
#Override
public AdsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final View view = layoutInflater.inflate(R.layout.avatars, parent, false);
return new AdsViewHolder(view);
}
#Override
public void onBindViewHolder(AdsViewHolder holder, int position) {
holder.CV_item.setTag(position);
holder.img_avatar.setImageResource(dataList.get(position).getImage());
if(mItemSelected==position){
//code for image selected.
holder.CV_item.setVisibility(View.VISIBLE);
}else{
//code for image unselected.
holder.CV_item.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return dataList.size();
}
class AdsViewHolder extends RecyclerView.ViewHolder {
ImageView img_avatar;
CardView CV_item;
AdsViewHolder(View itemView) {
super(itemView);
//-----------------------------------------------------------
img_avatar = (ImageView) itemView.findViewById(R.id.img_avatar);
CV_item = (CardView) itemView.findViewById(R.id.cardView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mItemSelected=getAdapterPosition();
notifyDataSetChanged();
}
});
}
}
}
Replace this code
if (CV_Prev != null && CV_Prev.findViewById(R.id.img_select).getVisibility() == View.VISIBLE) {
CV_Prev.findViewById(R.id.img_select).setVisibility(View.INVISIBLE);
}
if(SelectCardView.findViewById(R.id.img_select).getVisibility() == View.INVISIBLE)
SelectCardView.findViewById(R.id.img_select).setVisibility(View.VISIBLE);
or you can create a global variable to store the clicked position and change its Visibility after second click
See if this helps: https://stackoverflow.com/a/46641850/4469112 I just explained how to properly set the on click listeners to recycler view's adapter and view holders. The important part is to set the listener to the view holder (and no directly to the view), and then to let the view holder to set itself as listener to the passed view. Remember the view is recycled and the content is binded to it at runtime (when you scroll in your case), that's why you're seeing the selection 'transferred' to the other items.
Related
I want to create tags using RecyclerView that can be selected to produce results.
What I want to achieve is when I tap on Books TextView on the top-right must change to Books immediately. In my case when I tap on Books it just stays in Art and it replaces Art with Books only when I replace my fragments. I also highlight buttons when clicked (changing border from grey to black) but after changing fragments highlights return back to Art again. Shortly I want to highlight button when clicked and change TextView content based on clicked button text. I partly achieved it by using interfacews but didn't get what I wanted. I provided codes below:
TrendCategoryTagsAdapter.java:
public class TrendCategoryTagsAdapter extends FirestoreRecyclerAdapter<CategorySelection, TrendCategoryTagsAdapter.TrendCategoryTagsHolder> {
Context context;
onCategoryTagClicked onCategoryTagClicked;
int row_index;
public TrendCategoryTagsAdapter(#NonNull FirestoreRecyclerOptions<CategorySelection> options, Context context, com.rajabmammadli.paragrafredesign.Interface.onCategoryTagClicked onCategoryTagClicked) {
super(options);
this.context = context;
this.onCategoryTagClicked = onCategoryTagClicked;
}
#Override
protected void onBindViewHolder(#NonNull final TrendCategoryTagsHolder holder, final int position, #NonNull CategorySelection model) {
holder.categoryNameText.setText(model.getCategoryName());
holder.categoryNameContainer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
row_index = position;
notifyDataSetChanged();
}
});
if (row_index == position) {
holder.categoryNameContainer.setBackground(ContextCompat.getDrawable(context, R.drawable.black_rounded_bg));
onCategoryTagClicked.onTagClick(holder.categoryNameText.getText().toString());
} else {
holder.categoryNameContainer.setBackground(ContextCompat.getDrawable(context, R.drawable.grey_rounded_bg));
}
}
#NonNull
#Override
public TrendCategoryTagsHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.trendcategory_cell, parent, false);
return new TrendCategoryTagsAdapter.TrendCategoryTagsHolder(v);
}
public static class TrendCategoryTagsHolder extends RecyclerView.ViewHolder {
RelativeLayout categoryNameContainer;
TextView categoryNameText;
public TrendCategoryTagsHolder(#NonNull View itemView) {
super(itemView);
categoryNameContainer = itemView.findViewById(R.id.categoryNameContainer);
categoryNameText = itemView.findViewById(R.id.categoryNameText);
}
}
}
trendcategory_cell.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp">
<RelativeLayout
android:id="#+id/categoryNameContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/grey_rounded_bg">
<TextView
android:id="#+id/categoryNameText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/gilroyregular"
android:padding="15dp"
android:text="categoryname"
android:textColor="#android:color/black" />
</RelativeLayout>
</RelativeLayout>
TrendingFragment.java:
public class TrendingFragment extends Fragment implements onCategoryTagClicked {
RecyclerView trendingCategoryRV;
TextView noPostTV, selectedCategoryText;
FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference categoryRef;
String selectedCategory = "Art";
TrendCategoryTagsAdapter trendCategoryTagsAdapter;
public TrendingFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_trending, container, false);
trendPostRV = view.findViewById(R.id.trendPostRV);
trendingCategoryRV = view.findViewById(R.id.trendingCategoryRV);
noPostTV = view.findViewById(R.id.noPostTV);
selectedCategoryText = view.findViewById(R.id.selectedCategoryText);
selectedCategoryText.setText(selectedCategory);
setUpTrendCategoryTagsRV();
setUpTrendingPostRV();
// Inflate the layout for this fragment
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
trendCategoryTagsAdapter.startListening();
}
#Override
public void onDestroyView() {
super.onDestroyView();
trendCategoryTagsAdapter.stopListening();
}
private void setUpTrendCategoryTagsRV() {
categoryRef = db.collection("Categories");
Query query = categoryRef.orderBy("categoryName", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<CategorySelection> options = new FirestoreRecyclerOptions.Builder<CategorySelection>()
.setQuery(query, CategorySelection.class)
.build();
trendCategoryTagsAdapter = new TrendCategoryTagsAdapter(options, getContext(), this);
trendingCategoryRV.setNestedScrollingEnabled(false);
final LinearLayoutManager trendingTagsLM = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
trendingCategoryRV.setLayoutManager(trendingTagsLM);
trendingCategoryRV.setAdapter(trendCategoryTagsAdapter);
trendCategoryTagsAdapter.notifyDataSetChanged();
}
#Override
public void onTagClick(String categoryName) {
selectedCategory = categoryName;
}
}
Any help will be appreciated. Thanks in advance
I have a RecyclerView and an adapter which has an ArrayList of Person model.
When the app starts I type the names in the EditText and press the button:
1)
2)
3)
4)
But when I log personList data in the activity, the result is:
personList: Item 0 -----> Alireza
personList: Item 1 -----> Tohid
personList: Item 2 -----> Alireza
First item is removed and last item is repeated in the first one. (My adapter is more complex, but I have reduced the code and views to focus on the main problem)
Adapter Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="#+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name" />
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add" />
</LinearLayout>
Adapter Class:
public class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<Person> mList;
private View.OnClickListener listener;
public TestAdapter(ArrayList<Person> mList, View.OnClickListener listener) {
this.mList = mList;
this.listener = listener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_adapter_item, parent, false);
if (viewType == Person.State.FIRST.getValue()) {
return new FirstViewHolder(itemView);
} else { // if (viewType == Person.State.SECOND.getValue())
return new SecondViewHolder(itemView);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
final int pos = holder.getAdapterPosition();
final Person person = mList.get(pos);
if (holder instanceof FirstViewHolder) {
final FirstViewHolder h = (FirstViewHolder) holder;
h.tvName.setVisibility(View.GONE);
h.etName.setVisibility(View.VISIBLE);
h.etName.setText("");
h.etName.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
person.setFirstName(s.toString());
}
#Override
public void afterTextChanged(Editable s) {
}
});
h.btnAccept.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
person.setState(Person.State.SECOND);
notifyItemChanged(pos);
listener.onClick(v);
}
});
} else { // if (holder instanceof SecondViewHolder)
final SecondViewHolder h = (SecondViewHolder) holder;
h.tvName.setVisibility(View.VISIBLE);
h.tvName.setText(person.getFirstName());
h.etName.setVisibility(View.GONE);
h.btnAccept.setVisibility(View.GONE);
}
}
private static class FirstViewHolder extends RecyclerView.ViewHolder {
private TextView tvName;
private EditText etName;
private Button btnAccept;
private FirstViewHolder(#NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tv_name);
etName = itemView.findViewById(R.id.et_name);
btnAccept = itemView.findViewById(R.id.button);
}
}
private static class SecondViewHolder extends RecyclerView.ViewHolder {
private TextView tvName;
private EditText etName;
private Button btnAccept;
SecondViewHolder(#NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tv_name);
etName = itemView.findViewById(R.id.et_name);
btnAccept = itemView.findViewById(R.id.button);
}
}
#Override
public int getItemCount() {
return mList.size();
}
#Override
public int getItemViewType(int position) {
return mList.get(position).getState().getValue();
}
}
Related Part of Activity:
ArrayList<Person> personList = new ArrayList<>();
personList.add(new Person("", Person.State.FIRST));
adapter = new TestAdapter(personList, new View.OnClickListener() {
#Override
public void onClick(View v) {
if (personList.size() < 3) {
personList.add(new Person("", Person.State.FIRST));
adapter.notifyItemInserted(personList.size());
} else {
for (int i = 0; i < personList.size(); i++) {
Log.i("personList", "Item " + i + " -----> " + personList.get(i).getName());
}
}
}
});
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
What's the problem? And how can I achieve correct and real ArrayList Data?
your problem lies with addTextChangedListener, when new items are added in recycler view old TextChangedListeners arent removed. and they keep changing your list items values. you should remove those listeners when state is changed to SECOND state(which is surprisingly tricky look at this How to remove all listeners added with addTextChangedListener). or you should add this validation when onTextChanged is triggered. if (person.getState() == Person.State.FIRST)
person.setFirstName(s.toString());
Code of the illustration:
mLinearLayoutManager = new LinearLayoutManager(this);
mLinearLayoutManager.setReverseLayout(true);
mLinearLayoutManager.setStackFromEnd(true);
mMessageRecyclerView.setLayoutManager(mLinearLayoutManager);
See illustration here
How can I add new items (in my case, messages) to the bottom of Recycler View and still keep the "gravity" of the view to the top?
So, what works now is the following:
The gravity of the view is at the top. That's good! ✓
What doesn't work:
New messages are added to the top of the view. That's bad ×
I want them to be added at the bottom of the view (after the previous message) like so:
See here
Try removing these two lines or setting them false
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
setStackFromEnd will set the view to show the last element, the layout direction will remain the same whereas setReverseLayout will change the order of the elements added by the Adapter.
Try using this to move your RecyclerView and EditText up when keyboard appears
<activity name="YourActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
//stateHidden -> keyboard is hidden when you first open the activity
//adjustResize -> this will adjust the layout resize option
...
</activity>
in AndroidManifest.xml.
To hook the RecyclerView at top
<android.support.v7.widget.RecyclerView
android:id="#+id/messageRecyclerViewRep"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="#+id/linearLayout3"
android:layout_marginLeft="36dp"
android:scrollbars="vertical" />
To put the recyclerView at bottom first and push it up as the keyboard pops up.
<LinearLayout
android:id="#+id/recyclerContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/linearLayout3"
android:layout_above="#+id/linearLayout"
android:gravity="bottom">
<android.support.v7.widget.RecyclerView
android:id="#+id/messageRecyclerViewRep"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="36dp"
android:scrollbars="vertical" />
</LinearLayout>
To scroll the recyclerView to bottom when keyboard pops up i.e. when the recycler view's layout is changed ( You can do the same thing on Edit Text active or focused or clicked or something like that. I've done it on recycler view's layout change. )
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
recyclerView.postDelayed(new Runnable() {
#Override
public void run() {
recyclerView.smoothScrollToPosition(mAdapter.getItemCount());
}
}, 100);
}
}
});
setReverseLayout(true) this will reverse the item traversal & layout order.The first item will come to end not view or content.
setStackFromEnd(true) this will fill the recycler list content starting from the bottom of the view.
Need to setStackFromEnd(true) not setReverseLayout(true)
And in XML Recyclerview height should be match_parent
Below i have given working code.
activity xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="#+id/btnAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rcList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="true" />
</LinearLayout>
list item xml layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:gravity="center_vertical">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content" android:textSize="23sp"
android:layout_height="wrap_content" android:textColor="#4a4883"
android:text="Test Text" />
</FrameLayout>
Adapter class
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private static final String TAG = "CustomAdapter";
private ArrayList<String> mDataSet;
private int size = 0;
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View v) {
super(v);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Element " + getAdapterPosition() + " clicked.");
}
});
textView = (TextView) v.findViewById(R.id.textView);
}
public TextView getTextView() {
return textView;
}
}
public CustomAdapter(ArrayList<String> dataSet) {
mDataSet = dataSet;
if (mDataSet != null && !mDataSet.isEmpty()) {
size = mDataSet.size();
}
}
public void refreshData(String add) {
if (!TextUtils.isEmpty(add)) {
mDataSet.add(add);
size = mDataSet.size();
notifyDataSetChanged();
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view.
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.list_item, viewGroup, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
Log.d(TAG, "Element " + position + " set.");
viewHolder.getTextView().setText(mDataSet.get(position));
}
#Override
public int getItemCount() {
return size;
}
}
Activity class
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
protected CustomAdapter mAdapter;
protected LinearLayoutManager mLayoutManager;
protected ArrayList<String> listString = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.rcList);
mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
/**
*setStackFromEnd true will fill the content(list item) from the bottom of the view
*/
mLayoutManager.setStackFromEnd(true);
mLayoutManager.setReverseLayout(true);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
findViewById(R.id.btnAdd).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int temp = mAdapter.getItemCount() + 1;
mAdapter.refreshData("Test text " + temp);
mRecyclerView.smoothScrollToPosition(mAdapter.getItemCount());
}
});
mAdapter = new CustomAdapter(listString);
mRecyclerView.setAdapter(mAdapter);
}
}
The simplest way to achieve this would be to load the elements into the Recyclerview in the reverse order to how they are entered in the Firebase Database.
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
Log.d(TAG, "Element " + position + " set.");
viewHolder.getTextView().setText(mDataSet.get(getItemCount() - 1 -position));
}
What this would do is that the item which is inserted last will get displayed at bottom since you are loading the items from top. No need for any change in any of the XMLs or any other code.
I had same issues with Recylerview not resizing itself when the keyboard pops up and therefore I resorted to this method.
All the best!!
I have implemented and here is my Implementation using FirebaseRecyclerView
you need to set this setStackFromEnd=true and setReverseLayout=true
my xml
Recycler View Holder
public class TestingFirebaseHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
private TextView mTextView2;
public TestingFirebaseHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.textViewTesting);
mTextView2 = itemView.findViewById(R.id.textViewTesting2);
}
public void setTextView(String text,String text2)
{
mTextView.setText(text);
mTextView2.setText(text2);
}
}
Testing Class
public class TestingUser {
public String UserName;
public String mUid;
public TestingUser() {
}
public TestingUser(String userName, String uid) {
UserName = userName;
mUid = uid;
}
}
Activity Code
private EditText mEditText;
private RecyclerView mRecyclerView;
private FirebaseRecyclerAdapter<TestingUser,TestingFirebaseHolder> mAdapter;
private FirebaseUser mUser;
private DatabaseReference mReference;
mEditText = findViewById(R.id.testingEditText);
mRecyclerView = findViewById(R.id.hello_rec);
mUser = FirebaseAuth.getInstance().getCurrentUser();
mReference = FirebaseDatabase.getInstance().getReference();
LinearLayoutManager ll = new LinearLayoutManager(this);
ll.setReverseLayout(true); // set this
ll.setStackFromEnd(true); // set this
mRecyclerView.setLayoutManager(ll);
Query query = mReference.child("Testing").child(mUser.getUid()).orderByValue();
mAdapter = new FirebaseRecyclerAdapter<TestingUser, TestingFirebaseHolder>(
TestingUser.class,R.layout.testing_recycler_layout,TestingFirebaseHolder.class,query
) {
#Override
protected void populateViewHolder(TestingFirebaseHolder viewHolder, TestingUser model, int position) {
viewHolder.setTextView(model.mUid,model.UserName);
}
};
mRecyclerView.setAdapter(mAdapter);
public void buttonClick(View view) {
if(!mEditText.getText().toString().isEmpty())
{
TestingUser user = new TestingUser("Salman",mEditText.getText().toString());
mReference.child("Testing").child(mUser.getUid()).push().setValue(user);
mEditText.setText("");
}
}
Result is
link
Try this, it works for me.
mLinearLayoutManager = new LinearLayoutManager(this);
mLinearLayoutManager.stackFromEnd(true)
mLinearLayoutManager.isSmoothScrollbarEnabled(true)
mMessageRecyclerView.setLayoutManager(mLinearLayoutManager);
How do i insert a "onclicklistener" on the imageviews in a cardview?
Context: I extracted frames from a video using ffmpeg, and have images displayed in a cardview, now i need to set a onclicklistener to get the bitmap of the image I clicked.
If i simply applied onClickListener to the view, like so:
view.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
// item clicked
}
});
Will it give me the correct image bitmap that i clicked?
I currently display images in a cardview using an adapter:
Activity
public class PreviewImageActivity extends AppCompatActivity {
private static final String FILEPATH = "filepath";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gallery);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
TextView tvInstruction=(TextView)findViewById(R.id.tvInstruction) ;
GridLayoutManager lLayoutlLayout = new GridLayoutManager(PreviewImageActivity.this, 4);
RecyclerView rView = (RecyclerView)findViewById(R.id.recycler_view);
rView.setHasFixedSize(true);
rView.setLayoutManager(lLayoutlLayout);
String filePath = getIntent().getStringExtra(FILEPATH);
ArrayList<String> f = new ArrayList<String>();
File dir = new File(filePath);
tvInstruction.setText("Images stored at path "+filePath);
File[] listFile;
listFile = dir.listFiles();
for(File e:listFile)
{
f.add(e.getAbsolutePath());
}
PreviewImageAdapter rcAdapter = new PreviewImageAdapter( f);
rView.setAdapter(rcAdapter);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// handle arrow click here
if (item.getItemId() == android.R.id.home) {
finish(); // close this activity and return to preview activity (if there is any)
}
return super.onOptionsItemSelected(item);
}
}
Adapter
public class PreviewImageAdapter extends RecyclerView.Adapter<PreviewImageAdapter.MyViewHolder> {
private ArrayList<String> paths;
public PreviewImageAdapter( ArrayList<String> paths) {
this.paths = paths;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_gallery, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Bitmap bmp = BitmapFactory.decodeFile(paths.get(position));
holder.ivPhoto.setImageBitmap(bmp);
}
#Override
public int getItemCount() {
return paths.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private ImageView ivPhoto;
public MyViewHolder(View itemView) {
super(itemView);
ivPhoto = (ImageView) itemView.findViewById(R.id.ivPhoto);
}
}
}
XML
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardUseCompatPadding="true"
card_view:cardCornerRadius="#dimen/dp4"
android:layout_margin="#dimen/dp8">
<ImageView
android:id="#+id/ivPhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
/>
</android.support.v7.widget.CardView>
You should add the OnClickListener in the PreviewImageAdapter onBindViewHolder method, where you have access to the imageview.
holder.ivPhoto.setOnClickListener(new View.OnClickListener())...
another workaround is to implement onClickListener on your ViewHolder class and then switch/case the view ids for different actions like:
class viewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
...
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.v1:
// Your code
break;
case R.id.v2:
// Your Code
break;
}
}
}
I am building a sudoku in android ,so how do i register all the textviews
for e.g :
button1=(Button)findViewByid(R.id.btn1)
Do i need to write 81 such statements to register every TextView
you can add the views in java code when app running.
you can write your textview's xml statement in an xml file alone, and then inflate it.
In this way, you don't need the view id, because you already have its reference.
grid.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
/>
MyActivity.java
ArrayList<TextView> list = new ArrayList<>();
for (int i = 0; i < 81; i++) {
TextView view = (TextView)LayoutInflater.from(ItemDragAndSwipeUseActivity.this).inflate(R.layout.grid, null);
list.add(view);
}
// then attach these views to the layout with addView()
you can use grid view with an adapter instead of text views
grid item
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="13sp"
android:text="#string/country_name"
android:textColor="#color/accent_color"
android:gravity="center"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:background="#color/color_primary_dark"/>
</RelativeLayout>
Adapter class
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolders> {
private List<ItemObject> itemList;
private Context context;
public RecyclerViewAdapter(Context context, List<ItemObject> itemList) {
this.itemList = itemList;
this.context = context;
}
#Override
public RecyclerViewHolders onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, null);
RecyclerViewHolders rcv = new RecyclerViewHolders(layoutView);
return rcv;
}
#Override
public void onBindViewHolder(RecyclerViewHolders holder, int position) {
holder.textView.setText(itemList.get(position).getName());
}
#Override
public int getItemCount() {
return this.itemList.size();
}
public class RecyclerViewHolders extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView textView;
public RecyclerViewHolders(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
textView = (TextView)itemView.findViewById(R.id.textView);
}
#Override
public void onClick(View view) {
Toast.makeText(view.getContext(), "Clicked Position = " + getPosition(), Toast.LENGTH_SHORT).show();
}
}
}
Activity class
public class MainActivity extends ActionBarActivity {
private GridLayoutManager lLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle(null);
Toolbar topToolBar = (Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(topToolBar);
topToolBar.setLogo(R.drawable.logo);
topToolBar.setLogoDescription(getResources().getString(R.string.logo_desc));
List<ItemObject> rowListItem = getAllItemList();
lLayout = new GridLayoutManager(MainActivity.this, 4);
RecyclerView rView = (RecyclerView)findViewById(R.id.recycler_view);
rView.setHasFixedSize(true);
rView.setLayoutManager(lLayout);
RecyclerViewAdapter rcAdapter = new RecyclerViewAdapter(MainActivity.this, rowListItem);
rView.setAdapter(rcAdapter);
}
private List<ItemObject> getAllItemList(){
List<ItemObject> allItems = new ArrayList<ItemObject>();
allItems.add(new ItemObject("1");
allItems.add(new ItemObject("2");
allItems.add(new ItemObject("3");
return allItems;
}
}
You don't have to use IDs for this. You can just create a View (Container) in XML and fill it using a for loop completely without using IDs. You can access them afterwards via their index in the Container.
I hope this helped.