I have one CardView with custom selector which include focus state to true
<android.support.v7.widget.CardView
android:id="#+id/cvColor"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="center"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="2dp"
android:foreground="#drawable/card_color_size"
android:minHeight="50dp"
android:visibility="gone"
app:cardCornerRadius="3dp"
app:cardElevation="4dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="true" >
My need is when I click on card it should be clicked as well as focused too. so I did something like this.
#Override
public void onBindViewHolder(final ViewHolderCard1 viewHolder,
final int position) {
viewHolder.size.setVisibility(View.VISIBLE);
viewHolder.tvSize.setText(size.get(position).get("size"));
viewHolder.size.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
posSize = position;
Toast.makeText(getApplicationContext(), "" + posSize,
Toast.LENGTH_SHORT).show();
viewHolder.size.setFocusableInTouchMode(true);
}
});
}
now when I click once of the item of RecycleView it works fine, both Toast and focus happens same time.
But as soon as I click next item it just like on first click card perform click event and on second it gets focus.
anyone has idea what should I do?
Try this code.
#Override
public void onBindViewHolder(final ViewHolderCard1 viewHolder,
int position) {
viewHolder.size.setVisibility(View.VISIBLE);
viewHolder.tvSize.setText(size.get(position).get("size"));
viewHolder.size.setOnClickListener(new Listener(position, viewHolder.size));
}
class Listener implements OnClickListener {
private int position;
private ImageView imageView;
private CardView cardview;
Listener(int position, CardView cardview) {
this.position = position;
this.imageView = imageView;
this.data = data;
}
#SuppressLint("UseValueOf")
#Override
public void onClick(View v) {
posSize = position;
Toast.makeText(getApplicationContext(), "" + posSize,
Toast.LENGTH_SHORT).show();
CardView.setFocusableInTouchMode(true);
}
}
I solved this few days back do I am posting here my answer if any one faeces this problem.
SparseBooleanArray did the trick in my case.
First of all declare SparseBooleanArray in your Adapter Class. and Initialize it in your constructor of adapter class.
private ArrayList<HashMap<String, String>> size;
public final SparseBooleanArray selectedItems;
public Size(ArrayList<HashMap<String, String>> title) {
this.size = title;
selectedItems = new SparseBooleanArray(size.size());
}
Now I use setSelected for focusing selected card. inside onBindViewHolder (or getView in case you are using BaseAdapter).
viewHolder.size.setSelected(selectedItems.get(position));
viewHolder.size.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
posSize = position;
if (selectedItems.size() == 0) {
selectedItems.put(position, true);
} else if (selectedItems.size() > 0) {
selectedItems.clear();
selectedItems.put(position, true);
}
notifyDataSetChanged();
}
});
Happy Coding.
Related
I have a ViewPager2 contains ScrollView.
What the problem is, when I try to get ScrollView of current page in onPageSelected(), it doesn't work.
Here I'd like to set previous scrollY to the ScrollView when user back to see selected page. (because scrollY is reset for some reason before that)
My code is below.
ViewPagerAdapter.java (edited)
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewHolder> {
private LayoutInflater mInflater;
private List<String> mText;
private ViewPager2 pager2;
MainActivity main;
public ViewPagerAdapter(Context context, List<String> data, ViewPager2 pager2, MainActivity main){
this.mInflater = LayoutInflater.from(context);
this.mText = data;
this.pager2 = pager2;
this.main = main;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view = mInflater.inflate(R.layout.tab_scroll_item, parent, false);
return new ViewPagerAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, int position){
holder.scrollView.setTag("scv_tab" + position);
holder.scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(scrollY != 0) {
main.setScrollY(scrollY, getPosition());
}
System.out.println("onScrollChanged : " + scrollY);
}
});
holder.textView.setEnabled(false);
holder.textView.setEnabled(true);
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
holder.textView.setTag("tv_tab" + position);
}
#Override
public int getItemCount(){
return mText.size();
}
protected int getPosition(){
return pager2.getCurrentItem();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ScrollView scrollView;
TextView textView;
public ViewHolder(View itemView){
super(itemView);
scrollView = itemView.findViewById(R.id.tab_scroll);
textView = itemView.findViewById(R.id.tab_textview);
}
}
[REVICED]onBindViewHolder
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
holder.scrollView.setTag("scv_tab" + position);
holder.scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(scrollY != 0) {
main.storeScrollY(scrollY, position);
}
System.out.println("onScrollChanged : " + scrollY);
}
});
int y = main.retrieveScrollY(position);
holder.scrollView.setScrollY(y);
holder.textView.setEnabled(false);
holder.textView.setEnabled(true);
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
holder.textView.setTag("tv_tab" + position);
}
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
...
mPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if(!searchView.isIconified()){
searchView.setIconified(true);
}
if(highlightModel.getHighlitedOrNot(position)){
searchText.deleteTextHighlight(position);
highlightModel.setHighlitedOrNot(position, false);
}
int positionY[] = getScrollFromViewModel();
ScrollView sv = findScrollView(); // HERE IS THE PROBLEM
if(sv != null) {
sv.setScrollY(positionY[position]);
}else{
System.out.println("sv is null"); // ALWAYS SHOWS NULL
}
}
});
...
searchView = (SearchView) findViewById(R.id.searchview);
searchView.setOnSearchClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
fab.setVisibility(View.VISIBLE);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int actualResult = searchText.scrollToHighlightedWord(findTextView()
, findScrollView() // here findScrollView() works perfectly.
, searchResultIndex);
if(actualResult == (searchResultIndex + 1)) {
++searchResultIndex;
}else if(actualResult == searchResultIndex){
showToastAtMain("last word");
}else{
forOnClose();
}
}
});
}
});
private ScrollView findScrollView(){
ScrollView sv = mPager2.findViewWithTag("scv_tab" + mPager2.getCurrentItem());
return sv;
}
tab_scroll_item.xml (edited)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/tab1_layout">
<ScrollView
android:id="#+id/tab_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tab_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:lineSpacingExtra="7dp"
android:textIsSelectable="true"
android:paddingStart="9dp"
android:paddingTop="9dp"
android:paddingEnd="9dp"/>
</ScrollView>
</LinearLayout>
Any advice is truly appreciated. Thank you for reading this long question.
onPageSelected will be called way earlier than selected page drawing mechanism, before any onCreateViewHolder and onBindViewHolder. I would advise you to "incject" somehow your data into your "page" (is it View or whole Fragment? that makes big difference) and in your case set this scroll position inside onBindViewHolder. This will make rendering of RecyclerView draw even first frame in correct scrolled position. Your way, even when you will wait some time for RecyclerView drawing, will make that onBindViewHolder will draw first frame on 0 scroll and your method will scroll a bit in next frames - this will be visible like some blink or fast-autoscroll (I think this behavior isn't intended)
edit - add proper method for setting scroll for your Adapter even before super call
#Override
public void onPageSelected(int position) {
int positionY[] = getScrollFromViewModel();
adapter.setScrollForPosition(positionY[position]);
super.onPageSelected(position);
...
for adapter
private final HashMap<Integer, Integer> scrollYHistory = new HashMap<>();
public void setScrollForPosition(int position, int scrollY){
scrollYHistory.put(position, scrollY);
}
use stored scrollY value in onBindViewHolder and clean entry in your HashMap
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
Integer scrollY = scrollYHistory.remove(position);
if (scrollY == null) scrollYa = 0; // may be null if not stored yet!
holder.scrollView.setScrollY(scrollY);
...
also remove unnecessary reference to MainActivity main in adapter's constructor and variable inside, it already gets Context reference and doesn't (shouldn't) need to know which Activity is creating it
edit - expanding comment:
instead of calling scrollView.setScrollY straight inside onBindViewHolder you should set up your TextView at first, then wait for rendering it and then scrolling to proper position
#Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
...
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
...
holder.textView.post(new Runnable(){
// this will be called after nearest drawing
#Override
public void run() {
Integer scrollY = scrollYHistory.remove(position);
if (scrollY == null) scrollYa = 0; // may be null if not stored yet!
holder.scrollView.setScrollY(scrollY);
}
});
}
Thanks to snachmsm, I got to know that onPageSelected will be called way earlier than ViewPager2 drawing. And for some reason I found my solution using mPager2.postDelayed().
After adding these, I got NO null ScrollView here.
mPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
...
//Here I added onPageScrollStateChanged() and postDelayed() in it.
#Override
public void onPageScrollStateChanged(int state){
if(state == ViewPager2.SCROLL_STATE_SETTLING){
mPager2.postDelayed(new Runnable() {
#Override
public void run() {
int position = mPager2.getCurrentItem();
int y = scrollModel.getPreviousScrollY(position);
ScrollView sv = findScrollView();
sv.scrollTo(0, y);
}
}, 50);
}
}
});
}
When button clicked, i must update a TextView in same position and I have done it, but 9th and 10th position of RecyclerView follow first position and second position. In other word, if I clicked first button position, First position of TextView is updated, but, 9th position of TextView also updated, It should be not updated. How to solve this?
I follow this link
here is my Adapter
class ProductsByStoreAdapter extends RecyclerView.Adapter<ProductsByStoreAdapter.ViewHolder> {
private ArrayList<Products> products;
ProductsByStoreAdapter(ArrayList<Products> productses) {
this.products = productses;
//products = CenterRepository.getCenterRepository()
//.getListOfProductsInShoppingList();
}
#Override
public ProductsByStoreAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.products_card_item, viewGroup, false);
return new ProductsByStoreAdapter.ViewHolder(view);
}
class ViewHolder extends RecyclerView.ViewHolder{
private TextView tv_product_name, tv_product_price, tv_product_quantity;
private ImageView im_product_image;
private ImageButton button_add_product, button_min_product;
private EditText e_note;
private LinearLayout layout_note;
ViewHolder(View view) {
super(view);
im_product_image = (ImageView)view.findViewById(R.id.product_image);
tv_product_name = (TextView)view.findViewById(R.id.product_name);
tv_product_price = (TextView)view.findViewById(R.id.product_price);
tv_product_quantity = (TextView)view.findViewById(R.id.product_quantity);
e_note = (EditText)view.findViewById(R.id.e_note);
layout_note = (LinearLayout)view.findViewById(R.id.layout_note);
this.button_add_product = (ImageButton)view.findViewById(R.id.button_add_product);
button_min_product = (ImageButton)view.findViewById(R.id.button_min_product);
}
}
#Override
public void onBindViewHolder(final ProductsByStoreAdapter.ViewHolder viewHolder, final int position) {
Glide.with(viewHolder.im_product_image.getContext())
.load(products.get(position).getImage_uri())
.centerCrop()
.crossFade()
//.placeholder(R.drawable.placeholder_main)
.into(viewHolder.im_product_image);
CurrencyFormats currencyFormat = new CurrencyFormats();
viewHolder.tv_product_name.setText(products.get(position).getName());
viewHolder.tv_product_price.setText(currencyFormat.toRupiah(products.get(position).getPrice()));
//viewHolder.tv_product_quantity.setText("0");
viewHolder.button_add_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//current object
Products tempObj = (products).get(position);
((ProductsByStoreActivity)view.getContext()).updateItemCount(true);
tempObj.setQuantity(String.valueOf(1));
viewHolder.tv_product_quantity.setText(tempObj.getQuantity());
}
});
viewHolder.button_min_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Products tempObj = (products).get(position);
viewHolder.tv_product_quantity.setText(CenterRepository
.getCenterRepository().getListOfProductsInShoppingList()
.get(indexOfTempInShopingList).getQuantity());
((ProductsByStoreActivity)view.getContext()).updateItemCount(false);
}
}
}else {
}
}
});
}
#Override
public int getItemCount() {
//return products.size();
return products == null ? 0 : products.size();
}}
You need to move your onclick listener into onCreateViewHolder.
final ProductsByStoreAdapter.ViewHolder viewHolder = new ProductsByStoreAdapter.ViewHolder(view);
button_add_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//current object
Products tempObj = products.get(viewHolder.getAdapterPosition(););
((ProductsByStoreActivity)view.getContext()).updateItemCount(true);
tempObj.setQuantity(String.valueOf(1));
viewHolder.tv_product_quantity.setText(tempObj.getQuantity());
}
});
return viewHolder;
You can do the same with the other onclicklistener
Update: You do not setText to your product_quantity textview in the BindView function, unless a button is clicked. this means its value will be recycled from other items. you should check with an if statement what is the quantity of the item and present it even without clicking.
Old and not correct answer:
I am not sure if this is the problem, but its an easy check, so try it out. There are 2 positions - the adapter position, and the layout position. I think maybe the position you are using (the one that came from the onBind function) is the latter. You want the adapter position, so try using getAdapterPosition() like this:
Products tempObj = (products).get(getAdapterPosition());
add below line to resolve the problem of 9th and 10th position of item
#Override
public int getItemViewType(int position)
{
return position;
}
So I am trying to make my cards clickable and as they are clicked it opens a new activity (not implemented in the code below though), here is my code:
For Interface
import android.view.View;
public interface ClickListener {
public void itemClicked(View view , int position);
}
For Adapter:
public class RVAdapter extends RecyclerView.Adapter<RVAdapter.SensorViewHolder> {
private ClickListener clicklistener = null;
List<SensorData> sensors;
RVAdapter(List<SensorData> sensors) {
this.sensors = sensors;
}
public class SensorViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView sensorName;
TextView sensorDesc;
ImageView sensorPhoto;
private LinearLayout main;
SensorViewHolder(final View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
sensorName = (TextView) itemView.findViewById(R.id.sensor_name);
sensorDesc = (TextView) itemView.findViewById(R.id.sensor_desc);
sensorPhoto = (ImageView) itemView.findViewById(R.id.sensor_photo);
main = (LinearLayout) itemView.findViewById(R.id.main);
main.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(itemView.getContext(), "Position:" + Integer.toString(getPosition()), Toast.LENGTH_SHORT).show();
if(clicklistener != null){
clicklistener.itemClicked(v, getAdapterPosition());
}
}
});
}
}
public void setClickListener(ClickListener clickListener){
this.clicklistener = clickListener;
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public SensorViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
SensorViewHolder pvh = new SensorViewHolder(v);
return pvh;
}
#Override
public void onBindViewHolder(SensorViewHolder SensorViewHolder, int i) {
SensorViewHolder.sensorName.setText(sensors.get(i).name);
SensorViewHolder.sensorDesc.setText(sensors.get(i).descriptor);
SensorViewHolder.sensorPhoto.setImageResource(sensors.get(i).iconID);
}
#Override
public int getItemCount() {
return sensors.size();
}
}
and here is the code for MainActivity:
public class MainActivity extends AppCompatActivity {
private List<SensorData> sensorData;
private RecyclerView rv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Find the recycler view for the <code>
rv = (RecyclerView) findViewById(R.id.rv);
//Initialising a linear layout manager
final LinearLayoutManager llm = new LinearLayoutManager(this);
rv.setLayoutManager(llm);
rv.setHasFixedSize(true);
initializeData();
initializeAdapter();
rv.setClickListener(this);
}
private void initializeData() {
sensorData = new ArrayList<>();
sensorData.add(new SensorData("Accelerometer", "Measures the acceleration of a moving or vibrating body.", R.mipmap.ic_accl));
sensorData.add(new SensorData("GPS Sensor", "Provides real-time user location and time information.", R.mipmap.ic_gps));
sensorData.add(new SensorData("Proximity Sensor", "Provides ambient light and proximity sensing.", R.mipmap.ic_als));
}
private void initializeAdapter() {
RVAdapter adapter = new RVAdapter(sensorData);
rv.setAdapter(adapter);
}
}
And yet it seems I cannot access setClickListener. I must mention I am quite a beginner in this.
Where have I mistaken? Is this the correct way to implement clicks on a card or I am missing something?
Also here is the MainActivity XML if needed.
<?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:padding="16dp"
android:id="#+id/main">
<android.support.v7.widget.RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/rv">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
and items.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cv"
app:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sensor_photo"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginRight="16dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sensor_name"
android:layout_toRightOf="#+id/sensor_photo"
android:layout_alignParentTop="true"
android:textSize="30sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sensor_desc"
android:layout_toRightOf="#+id/sensor_photo"
android:layout_below="#+id/sensor_name" />
</RelativeLayout>
</android.support.v7.widget.CardView>
EDIT: Just to avoid confusion, **I want the use to be able to click the cards and go to a new activity. ** How can I implement that in the simplest possible way? I am really confused on how to do it in the RecyclerView.
Thanks a lot and regards.
Since there is a difference between the old ListView and the new RecyclerView component, I have a reusable piece of code that I use to handle click events (both regular clicks and LongClicks):
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
#SuppressWarnings("CanBeFinal")
private GestureDetector mGestureDetector;
#SuppressWarnings("CanBeFinal")
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener)
{
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener()
{
#Override
public boolean onSingleTapUp(MotionEvent e)
{
return true;
}
#Override
public void onLongPress(MotionEvent e)
{
View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
if(childView != null && mListener != null)
{
mListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e)
{
View childView = view.findChildViewUnder(e.getX(), e.getY());
if(childView != null && mListener != null && mGestureDetector.onTouchEvent(e))
{
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent){}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
Then simply do this in your activity or fragment where you want to handle click events:
mRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, mRecyclerView, new RecyclerItemClickListener
.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
//start new activity here
}
#Override
public void onItemLongClick(View view, int position) {
}
}));
That should be the easiest way to do it!
I hope this helps you! Good luck and happy coding!
Basically you need to set the listener in the Viewholder
public class SensorViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView sensorName;
TextView sensorDesc;
ImageView sensorPhoto;
private LinearLayout main;
SensorViewHolder(final View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
//Add the following line below
cv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//The intent firing goes here
}
});
sensorName = (TextView) itemView.findViewById(R.id.sensor_name);
sensorDesc = (TextView) itemView.findViewById(R.id.sensor_desc);
sensorPhoto = (ImageView) itemView.findViewById(R.id.sensor_photo);
main = (LinearLayout) itemView.findViewById(R.id.main);
main.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(itemView.getContext(), "Position:" + Integer.toString(getPosition()), Toast.LENGTH_SHORT).show();
if(clicklistener != null){
clicklistener.itemClicked(v, getAdapterPosition());
}
}
});
}
}
Hope it helps.
To fix the compiler error, the correct way to set a OnClickListener is with
rv.setOnClickListener()
Note that the error is because you are missing the word On. Generally, you should use autocomplete in Android Studio to help avoid these types of issues. If autocomplete doesn't pop up automatically, you can push Ctrl-Space to get it.
As a side note, you can build your project by clicking Build -> Make Project in the main menu. This will give a window with the error messages where you can copy and paste more easily than the usual error balloons.
first of all set tag for your card in onBindViewHolder:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.cv.setTag(position);
}
then add on click listener for your card in your view holder in adapter:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public CardLayout cv;
public ViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(Rid.cv);
cv.setOnClickListener(this);
}
#Override
public void onClick(View v) {
//for find item that hold in list
int postition = (Integer) v.getTag();
...
}
In your findViewById you are casting the view to a RecyclerView and therefore you are referring the standard RecyclerView implementation of the Android framework. So this would never work.
The next thing is you are trying to set a listener to the RecyclerView but define the setListener method inside your adapter.
So what you should do is to either get a reference to your adapter via:
((RvAdapter) rv.getAdapter()).setClickListener();
after setting it or even better set the listener before you call
rv.setAdapter()
via
RvAdapter adapter = new RvAdapter();
adapter.setClickListener(listener)
I'm working on a simple restaurant POS system, and I had a problem suddenly come up. This problem has only occurred when I run it on a Genymotion tablet device. I do not have this problem on smaller genymotion devices or the 2 physical devices I've tried.
One of my activities uses 5 RecyclerViews to display the menu and sales summary. Every few item clicks, the RecyclerViews decide to just not update and do not properly display my items unless I scroll a bit on ANY of the RecyclerViews or click something. Even though they are not displayed properly, all item clicks register what should be shown (not what is actually shown).
For example, I click beef -> seafood -> chicken. The RecyclerView responsible for showing the menu items still shows seafood. If I click on anything, the correct chicken item gets selected and everything refreshes to what should show up. If I scroll on any RecyclerView (even an unrelated one), everything refreshes properly.
Activity onCreate():
...
cAdapter = new CategoryAdapter();
cAdapter.setOnItemClickListener(new OnItemClickAdapter<Category>() {
#Override
public void onItemClick(Category item, int position) {
miAdapter.setItems(item.getMenuItems());
va1.setDisplayedChild(0);
}
});
miAdapter = new MenuItemAdapter();
miAdapter.setOnItemClickListener(new OnItemClickAdapter<MenuItem>() {
#Override
public void onItemClick(MenuItem item, int position) {
salesOrder.addMenuItem(item);
oeAdapter.notifyDataSetChanged();
updateTotals();
}
});
...
Adapters:
public abstract class ItemRecyclerViewAdapter<T, TVH extends ItemViewHolder<T>> extends RecyclerView.Adapter<TVH> {
private ItemViewHolder.OnItemClickListener<T> listener;
protected List<T> items;
public ItemRecyclerViewAdapter() {
this(new ArrayList<T>());
}
public ItemRecyclerViewAdapter(List<T> items) {
this.items = items;
}
public final void setOnItemClickListener(ItemViewHolder.OnItemClickListener<T> listener) {
this.listener = listener;
}
public void setItems(List<T> items) {
this.items = items;
this.notifyDataSetChanged();
}
public void clear() {
this.items.clear();
this.notifyDataSetChanged();
}
protected abstract TVH createItemViewHolder(ViewGroup parent, int viewType);
#Override
public TVH onCreateViewHolder(ViewGroup parent, int viewType) {
TVH holder = createItemViewHolder(parent, viewType);
holder.setOnItemClickListener(listener);
return holder;
}
#Override
public void onBindViewHolder(TVH holder, int position) {
holder.setItem(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
}
ViewHolders:
public abstract class ItemViewHolder<T> extends RecyclerView.ViewHolder {
public ItemViewHolder(View itemView) {
super(itemView);
}
public interface OnItemClickListener<T> {
void onItemClick(T item, int position);
boolean onItemLongClick(T item, int position);
}
public final void setOnItemClickListener(final OnItemClickListener<T> listener) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null)
listener.onItemClick(getItem(), getAdapterPosition());
}
});
itemView.setOnLongClickListener(new View.OnLongClickListener(){
#Override
public boolean onLongClick(View v) {
return listener != null && listener.onItemLongClick(getItem(), getAdapterPosition());
}
});
}
public abstract T getItem();
public abstract void setItem(T item);
}
It SEEMS like it's a swapbuffer issue or some race condition I'm not seeing, but I thought I'd see if maybe I'm misunderstanding something first. Any ideas would be much appreciated!
I have that bug too.
After editing my activity_main.xml, I could fix it.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ulimbridge.recylerview.MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/recycler"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</android.support.constraint.ConstraintLayout>
Making RecyclerViews' height and width '0dp' was a solution.
I have a list view in left of the screen and on click of the item i want to update a text on the right half of the screen, what i want to do here is that to move the clicked item in center of the listview. Like if the item is on top and i click on it it automatically moves to the center of the list view, how I can do this? Any kind of help will be appreciated.
I have a listview in which 7 items are visible and on startup 4th item will be selected as this is in center of the visible items in listview and if there are n items and whichever item is selected by user will be in center of the visible items in listview. Like i have 10 items and on start 4th is selected and when user selects the 3rd item, nth item from listview should come to index zero and and 3rd will come to position 4. Similarly for every other selected item? Can any one provide a code snippet for this?
Change items order in ListView source Array and then call notifyDataSetChanged() in ListView Adapter
EDIT: Code sample
public class ListAdapter extends BaseAdapter{
private Activity activity;
private ArrayList<ListRowObject> listItems;
public ListAdapter(Activity activity){
this.activity = activity;
listItems = new ArrayList<ListRowObject>();
}
public void addItem(ListRowObject item){
listItems.add(item);
notifyDataSetChanged();
}
public void addItems(ArrayList<ListRowObject> items){
listItems = items;
notifyDataSetChanged();
}
public void clear(){
listItems = null;
listItems = new ArrayList<ListRowObject>();
notifyDataSetChanged();
}
#Override
public int getCount() {
return listItems.size();
}
#Override
public Object getItem(int position) {
return listItems.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder holder;
if(convertView == null){
holder = new ViewHolder();
convertView = activity.getLayoutInflater().inflate(R.layout.list_row, null);
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.bgLayout = (LinearLayout) convertView.findViewById(R.id.bgLayout);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
ListRowObject row = listItems.get(position);
if(row.isSelected())
holder.bgLayout.setBackgroundColor(Color.GRAY);
else
holder.bgLayout.setBackgroundColor(Color.WHITE);
holder.text.setText(row.getText());
return convertView;
}
}
//--------
public class ListRowObject {
private String text;
private int positionInList;
private boolean isSelected;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getPositionInList() {
return positionInList;
}
public void setPositionInList(int positionInList) {
this.positionInList = positionInList;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
//------
public class Main extends Activity {
private ListView listView;
private ListAdapter adapter;
private Activity activity;
private ArrayList<ListRowObject> items;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
activity = this;
initializeFormViews();
initializeOnClickEvents();
fillList();
}
private void initializeFormViews(){
listView = (ListView) findViewById(R.id.listView);
}
private void initializeOnClickEvents(){
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Toast.makeText(activity, "Pressed " +position, Toast.LENGTH_SHORT).show();
// unselect all rows
for(ListRowObject item : items){
item.setSelected(false);
}
int first = adapterView.getFirstVisiblePosition();
int last = adapterView.getLastVisiblePosition();
int centerPosition = (first + last) / 2;
// change bg for centerPosition row
adapterView.getChildAt(centerPosition).findViewById(R.id.bgLayout).setBackgroundColor(Color.GRAY);
changeItems(position, centerPosition);
}
});
}
private void changeItems(int pressedPosition, int centerPosition){
ListRowObject centerRow = items.get(centerPosition);
ListRowObject pressedRow = items.get(pressedPosition);
pressedRow.setSelected(true);
centerRow.setSelected(false);
items.remove(centerPosition);
items.add(centerPosition, pressedRow);
items.remove(pressedPosition);
items.add(pressedPosition, centerRow);
adapter.clear();
adapter.addItems(items);
}
private void fillList(){
adapter = new ListAdapter(activity);
items = new ArrayList<ListRowObject>();
items = getItems();
for(ListRowObject item : items){
adapter.addItem(item);
}
listView.setAdapter(adapter);
}
private ArrayList<ListRowObject> getItems(){
ArrayList<ListRowObject> result = new ArrayList<ListRowObject>();
for(int i = 0; i < 15; i++){
ListRowObject object = new ListRowObject();
object.setPositionInList(i);
object.setText("Item #" + i);
if(i != 4)
object.setSelected(false);
else
object.setSelected(true);
result.add(object);
}
return result;
}
}
//------
public class ViewHolder {
public TextView text;
public LinearLayout bgLayout;
}
list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="#+id/bgLayout">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="#+id/text"
android:textColor="#000000"
android:textSize="24dp"
android:gravity="center"/>
</LinearLayout>
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/listView"/>
</LinearLayout>
when you create ArrayAdapter for your listview you send a ListArray to it.when you want change content .you only change this listArray then when click your item you can change ListArray and call notifyDataSetChanged(); method your adapter.