I've implemented a RecyclerView with CardView. Each CardView has an ImageView which I want to change its background color depending on the result of a Query (Empty result set -> Grey / Non Empty result set -> Red), this is implemented on the onBindViewHolder of the RecyclerView Adapter.
Here's the Adapter's Code (I've removed most of the code for the sake of clarity):
public class FavDirsAdapter extends RecyclerView.Adapter<FavDirsAdapter.FavDirsViewHolder> {
private LayoutInflater mInflater;
private Cursor mCursor;
private Context mContext;
private int range;
private FragmentManager mFragmentManager;
public FavDirsAdapter(Context context, Cursor cursor, FragmentManager fm) {
mInflater = LayoutInflater.from(context);
mCursor = cursor;
mContext = context;
range = cursor.getCount();
mFragmentManager = fm;
}
#Override
public FavDirsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = mInflater.inflate(R.layout.item_fav_dirs_list, parent, false);
return new FavDirsViewHolder(view);
}
#Override
public void onBindViewHolder(final FavDirsViewHolder viewHolder, final int position) {
if (mCursor.moveToFirst()) {
mCursor.moveToPosition(position);
viewHolder.favDirsItemTextView.setText(mCursor.getString(mCursor.getColumnIndex(
(FilmoContract.FavDirEntry.COLUMN_DIR))));
getDirImage(viewHolder);
setScheduledFilmsColor(viewHolder);
}
}
#Override
public int getItemCount() {
if (mCursor.moveToFirst()) {
return range;
}
return 0;
}
class FavDirsViewHolder extends RecyclerView.ViewHolder{
TextView favDirsItemTextView;
ImageView favDirsItemImageView;
ImageView favDirsItemScheduledFilmsImage;
public FavDirsViewHolder(View itemView) {
super(itemView);
favDirsItemTextView = (TextView) itemView.findViewById(R.id.fav_dirs_item_text_view);
favDirsItemImageView = (ImageView) itemView.findViewById(R.id.fav_dirs_item_image_view);
favDirsItemScheduledFilmsImage =
(ImageView) itemView.findViewById(R.id.fav_dirs_item_scheduled_films_image_button);
}
}
private void setScheduledFilmsColor(FavDirsViewHolder viewHolder) {
new SetScheduledFilmsColor(viewHolder, mContext).execute();
}
private void getDirImage (FavDirsViewHolder viewHolder) {
new FetchFavDirsImage(viewHolder, mContext).execute();
}
}
The adapter receive a Cursor with a Films Director data list stored on a DB. Then assign each row data to a ViewHolder fileds.
Then I make an additional query to determine wheter each Director has movies scheduled soon, and depending on the query result, color the ImageView (favDirsItemScheduledFilmsImage) background.
I tried first to make the query on the onBindViewHolder Adapter's method, but I've found that all the ImageViews, where colored in the same color (Grey). So since there is a DB Query involved, I've tried to create an AsyncTask (SetScheduledFilmsColor) to do all that stuff. Here's the code:
public class SetScheduledFilmsColor extends AsyncTask<Void, Void, Boolean> {
private ImageView mImageButton;
private String mDirName;
private Context mContext;
boolean scheduledFilms;
static final String[] PROGRAM_COLUMNS = {
FilmoContract.FilmEntry._ID,
FilmoContract.FilmEntry.COLUMN_DATE,
FilmoContract.FilmEntry.COLUMN_TIME,
FilmoContract.FilmEntry.COLUMN_CYCLE,
FilmoContract.FilmEntry.COLUMN_TITLE,
};
public SetScheduledFilmsColor(FavDirsAdapter.FavDirsViewHolder viewHolder, Context context) {
mImageButton = viewHolder.favDirsItemScheduledFilmsImage;
//mImageButton = new ImageButton(mContext);
mDirName = viewHolder.favDirsItemTextView.getText().toString();
mContext = context;
}
#Override
protected Boolean doInBackground(Void... params) {
Uri filmoDirector = FilmoContract.FilmEntry.buildProgramUriWithDirector();
Cursor tempCursor = mContext.getContentResolver().query(
filmoDirector,
PROGRAM_COLUMNS,
mDirName,
null,
null
);
scheduledFilms = tempCursor.moveToFirst();
tempCursor.close();
return scheduledFilms;
}
#Override
protected void onPostExecute(Boolean scheduledFilms) {
super.onPostExecute(scheduledFilms);
mImageButton.setImageResource(R.drawable.ic_new_releases_white_24dp);
if (scheduledFilms) {
//mImageButton.getBackground().clearColorFilter();
mImageButton.getBackground().setColorFilter(
mContext.getResources().getColor(R.color.lafilmo_color), PorterDuff.Mode.MULTIPLY
);
} else {
//mImageButton.getBackground().clearColorFilter();
mImageButton.getBackground().setColorFilter(
mContext.getResources().getColor(R.color.dividers), PorterDuff.Mode.MULTIPLY
);
}
}
}
I'm changing the color in the onPostExecute method. However, even doing this asynchronously, it doesn't matter the query result (I'm sure the query and the onPostExecute condition are fine, I have debug that). Even more, each time I reload the RecyclerView Fragment while navigating through my App, the colored ImageViews are different each time.
I don't understand, how RecyclerView can maintain the reference correctly to the other CardView fields (the fileds on the viewHolder, like favDirsItemTextView or favDirsItemImageView) which are assigned on the onBindViewHolder, and not maintain a reference to the colored ImageView (favDirsItemScheduledFilmsImage).
Can anybody shed some light on this? Is there a better way to do this?
Thanks!
Related
My app was very laggy, so I decided to use an AsyncTask to do the heaviest operations inside it and so, the app wouldn't be so slow at changing tabs.
But now, it is behaving in a very weird way. Let me explain: I have a ViewPager2, and inside that ViewPager, I have a recyclerview.
I put an AsyncTask inside the ViewPager, because it is the heaviest operation done in the fragment, and in the adapter of that ViewPager, I retrieve some values from a Database via a class called DatabaseHelper which one that extends SQLiteOpenHelper and has this method.
public Cursor getAllTasksByList(int ListID)
{
SQLiteDatabase db = this.getWritableDatabase();
Cursor c = db.rawQuery("SELECT * FROM " + Db.Tables.Tasktable.TASKS_TABLE + " WHERE " + Db.Tables.Tasktable.COL_LIST_ID + " = " + ListID, null);
return c;
}
Because the DatabaseHelper only returns one Cursor, I use another class to keep the code organized, this class takes the Cursor as argument and returns a list of "ListItem". This class is called "FolderUtils" and contains the following method (which one that I use to populate my RecyclerView inside that is inside my ViewPager):
public ArrayList<TaskItem> getTasksByList(int ListID, Context context) {
ArrayList<TaskItem> tasks = new ArrayList<>();
DatabaseHelper d = new DatabaseHelper(context);
Cursor c = d.getAllTasksByList(ListID);
while (c.moveToNext()) {
int id = c.getInt(0);
int listid = c.getInt(1);
boolean checked = c.getInt(2) > 0;
String title = c.getString(3);
tasks.add(new TaskItem(id, listid, checked, title));
}
return tasks;
}
But here it is the problem, sometimes this List is empty, but another times, it just retrieves the first value of the that Table I look for, strangely, sometimes it returns wrong values and it only works sometimes if I move my ViewPager to another position or if I just put some breakpoints. Here is my Adapter code.
#Override
public void onBindViewHolder(#NonNull ListHolder holder, int position) {
new LoadData(mList.get(position), holder).execute();
}
#Override
public int getItemCount() {
return mList.size();
}
private class LoadData extends AsyncTask<Void, Void, Void> {
private ListItem item;
private ListHolder holder;
public LoadData(ListItem item, ListHolder holder) {
this.item = item;
this.holder = holder;
}
#Override
protected void onPreExecute(){
super.onPreExecute();
//I set the visibility to GONE so that the user can just see the final layout and not the layout "Building" itself.
holder.itemView.setVisibility(View.GONE);
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
setItems(item, holder); //setItems is for setting the UI Content.
AttachRecycler(holder, item); //AttachRecycler creates an adapter for the recyclerview with the TaskList values, and attaches it to the recyclerview inside the ViewPager item.
holder.itemView.setVisibility(View.VISIBLE); //Shows the finished item
}
#Override
protected Void doInBackground(Void... voids) {
SetList(item); //SetList is where it takes the values from database and adds it to the list.
return null;
}
}
private void SetList(ListItem item) {
TaskList = new ArrayList<>();
else if (Mode == 1)
{
//Mode by default is 1. The line below does gets executed, however, it returns the wrong values.
TaskList.addAll(FolderUtils.getInstance().getTasksByList(item.getID(), context));
}
private void AttachRecycler(ListHolder holder, ListItem item)
{
LinearLayoutManager manager = new LinearLayoutManager(context);
holder.recycler.setLayoutManager(manager);
adapter = new TaskAdapter(TaskList, item.getColor(), context, item.getID());
holder.recycler.setAdapter(adapter);
}
How could I fix this? Thank You.
Solved this by myself.
Solution was to make TaskList a private variable inside the LoadData class, not a private variable of the entire Adapter, this acts like a local variable for every item instance, removing the duplicates in some items.
I have tried loading the list using the ListView along with LoaderManager.LoaderCallbacks and custom CursorAdapter and it works fine. But I am trying to accomplish the same using RecyclerView along with custom RecyclerView.Adapter but I am getting this issue:
I am getting the list displayed for the first time but when I rotate the device the list disappears.
Here is the code, please have a look.
CatalogActivity
public class CatalogActivity extends AppCompatActivity implements ItemAdapter.OnItemClickListener,
LoaderManager.LoaderCallbacks<Cursor> {
private static final int ITEMS_LOADER_ID = 1;
public static final String EXTRA_ITEM_NAME = "extra_item_name";
public static final String EXTRA_ITEM_STOCK = "extra_item_stock";
#BindView(R.id.list_items)
RecyclerView mListItems;
private ItemAdapter mItemAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalog);
ButterKnife.bind(this);
setupListItems();
getLoaderManager().initLoader(ITEMS_LOADER_ID, null, this);
}
private void setupListItems() {
mListItems.setHasFixedSize(true);
LayoutManager layoutManager = new LinearLayoutManager(this);
mListItems.setLayoutManager(layoutManager);
mListItems.setItemAnimator(new DefaultItemAnimator());
mListItems.addItemDecoration(new DividerItemDecoration(this, LinearLayout.VERTICAL));
mItemAdapter = new ItemAdapter(getApplicationContext(), this);
mListItems.setAdapter(mItemAdapter);
}
#Override
public void OnClickItem(int position) {
Intent intent = new Intent(this, EditorActivity.class);
Item item = mItemAdapter.getItems().get(position);
intent.putExtra(EXTRA_ITEM_NAME, item.getName());
intent.putExtra(EXTRA_ITEM_STOCK, item.getStock());
startActivity(intent);
}
private ArrayList<Item> getItems(Cursor cursor) {
ArrayList<Item> items = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
int columnIndexId = cursor.getColumnIndex(ItemEntry._ID);
int columnIndexName = cursor.getColumnIndex(ItemEntry.COLUMN_NAME);
int columnIndexStock = cursor.getColumnIndex(ItemEntry.COLUMN_STOCK);
int id = cursor.getInt(columnIndexId);
String name = cursor.getString(columnIndexName);
int stock = Integer.parseInt(cursor.getString(columnIndexStock));
items.add(new Item(id, name, stock));
}
}
return items;
}
#Override
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
switch (loaderId) {
case ITEMS_LOADER_ID: {
String[] projection = {
ItemEntry._ID,
ItemEntry.COLUMN_NAME,
ItemEntry.COLUMN_STOCK
};
return new CursorLoader(
this,
ItemEntry.CONTENT_URI,
projection,
null,
null,
null
);
}
default:
return null;
}
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mItemAdapter.setItems(getItems(cursor));
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}
ItemAdapter
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemViewHolder> {
private ArrayList<Item> mItems;
private OnItemClickListener mOnItemClickListener;
private Context mContext;
public ItemAdapter(Context context, OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
mContext = context;
}
public void setItems(ArrayList<Item> items) {
if (items != null) {
mItems = items;
notifyDataSetChanged();
}
}
public ArrayList<Item> getItems() {
return mItems;
}
public interface OnItemClickListener {
void OnClickItem(int position);
}
public class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
#BindView(R.id.tv_item)
TextView tv_item;
#BindView(R.id.tv_stock)
TextView tv_stock;
public ItemViewHolder(#NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
int position = getAdapterPosition();
mOnItemClickListener.OnClickItem(position);
}
}
#NonNull
#Override
public ItemViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_inventory, parent, false);
return new ItemViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ItemViewHolder itemViewHolder, int position) {
final Item item = mItems.get(position);
itemViewHolder.tv_item.setText(item.getName());
itemViewHolder.tv_stock.setText(mContext.getString(R.string.display_stock, item.getStock()));
}
#Override
public int getItemCount() {
if (mItems == null) {
return 0;
} else {
return mItems.size();
}
}
}
I am not able to figure out the extact issue. Please help.
Briefly, the issue here is that, after rotation, you're being handed the same Cursor that you had previously looped over before the rotation, but you're not accounting for its current position.
A Cursor tracks and maintains its own position within its set of records, as I'm sure you've gathered from the various move*() methods it contains. When first created, a Cursor's position will be set to right before the first record; i.e., its position will be set to -1.
When you first start your app, the LoaderManager calls onCreateLoader(), where your CursorLoader is instantiated, and then causes it to load and deliver its Cursor, with the Cursor's position at -1. At this point, the while (cursor.moveToNext()) loop works just as expected, since the first moveToNext() call will move it to the first position (index 0), and then to each available position after that, until the end.
Upon rotation, however, the LoaderManager determines that it already has the requested Loader (determined by ID), which itself sees that it already has the appropriate Cursor loaded, so it just immediately delivers that same Cursor object again. (This is a major feature of the Loader framework – it won't reload resources it already has, regardless of configuration changes.) This is the crux of the issue. That Cursor has been left at the last position to which it was moved before the rotation; i.e., at its end. Consequently, the Cursor cannot moveToNext(), so that while loop just never runs at all, after the initial
onLoadFinished(), before rotation.
The simplest fix, with the given setup, would be to manually reposition the Cursor yourself. For example, in getItems(), change the if to moveToFirst() if the Cursor is not null, and change the while to a do-while, so we don't inadvertently skip over the first record. That is:
if (cursor != null && cursor.moveToFirst()) {
do {
int columnIndexId = cursor.getColumnIndex(ItemEntry._ID);
...
} while (cursor.moveToNext());
}
With this, when that same Cursor object is re-delivered, its position is kinda "reset" to position 0. Since that position is directly on the first record, rather than right before it (remember, initially -1), we change to a do-while, so that the first moveToNext() call doesn't skip the first record in the Cursor.
Notes:
I would mention that it is possible to implement a RecyclerView.Adapter to take a Cursor directly, similar to the old CursorAdapter. In this, the Cursor would necessarily be moved in the onBindViewHolder() method to the correct position for each item, and the separate ArrayList would be unnecessary. It'd take a little effort, but translating CursorAdapter to a RecyclerView.Adapter isn't terribly difficult. Alternatively, there are certainly solutions already available. (For example, possibly, this one, though I cannot vouch for it, atm, I often see a trusted fellow user recommend it often.)
I would also mention that the native Loader framework has been deprecated, in favor of the newer ViewModel/LiveData architecture framework in support libraries. However, it appears that the newest androidx library has its own internal, improved Loader framework which is a simple wrapper around said ViewModel/LiveData setup. This seems to be a nice, easy way to utilize the known Loader constructs while still benefiting from the recent architecture refinements.
Instead of LoaderManager.initLoader() call LoaderManager.restartLoader()
I am new in Android Development and working on a project where I need to call an API after every one second, in that API there is field "Amount"(dBID) which keeps on changing, so I need to update the latest Amount (dBID) in recyclerview.
In order to do so, I have called this API in a service after every interval of one second.
The data is Showing Properly no Issue.
But for Now I need to perform some action on the Old Amount and New Amount.
Action Required : I need to compare the old value (dBID) with the New Value (dBID).
If the New Value is greater then I need to change the Text Color of Amount (dBID) to BLUE.
If the New Value is smaller then I need to change the Text Color of Amount (dBID) to RED.
Tried to achieve this by storing the old data in a Variable and then Comparing it to the new Value.
Issue : This logic is working fine until there are 5 or less Items in recyclerview as soon as the sixth item is added the same logic does not work.
Help me if anyone knows how I can achieve this.
For Example you can refer an App Vertexfx : Quotes Tab.
Below is the Code which I Tried.
Adapter class of the RecyclerView:
public class QuoteAdapter extends RecyclerView.Adapter <QuoteAdapter.MyViewHolder>{
Context context;
List<QuoteData> data;
public QuoteAdapter(Context context,List<QuoteData> data)
{
this.data = data;
this.context = context;
}
class MyViewHolder extends RecyclerView.ViewHolder{
TextView time,symbol,sellmax,selllow,buymax,buylow,buy,sell,spread,lowtext,hightext;
LinearLayout layout,layoutbid,layoutask;
float currentbid,lastbid,currentask,lastask;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
time = itemView.findViewById(R.id.TVTime);
symbol = itemView.findViewById(R.id.TVSymbol);
sellmax = itemView.findViewById(R.id.TVSELLMAX);
selllow = itemView.findViewById(R.id.TVSELLLOW);
buymax = itemView.findViewById(R.id.TVBUYMAX);
buylow = itemView.findViewById(R.id.TVBUYHIGH);
buy = itemView.findViewById(R.id.TVBUY);
sell = itemView.findViewById(R.id.TVSELL);
spread = itemView.findViewById(R.id.TVSpread1);
lowtext = itemView.findViewById(R.id.low);
hightext = itemView.findViewById(R.id.high);
layout = itemView.findViewById(R.id.layout);
layoutbid = itemView.findViewById(R.id.LLBid);
layoutask = itemView.findViewById(R.id.LLAsk);
currentbid = 0;
lastbid = 0;
currentask = 0;
lastask = 0;
}
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.quotelist,viewGroup,false);
return new MyViewHolder(view);
}
#SuppressLint("ResourceAsColor")
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, final int i) {
final QuoteData data1 = data.get(i);
myViewHolder.time.setText(data1.dLut);
myViewHolder.symbol.setText(data1.dSymbol);
myViewHolder.sellmax.setText(data1.dBid); //Bid
myViewHolder.selllow.setText(data1.dLow);
myViewHolder.buymax.setText(data1.dAsk); //ask
myViewHolder.buylow.setText(data1.dHigh);
myViewHolder.currentbid = Float.parseFloat((data1.dBid));
myViewHolder.currentask = Float.parseFloat((data1.dAsk));
if (myViewHolder.currentbid > myViewHolder.lastbid)
{
myViewHolder.sellmax.setTextColor(Color.BLUE);
}
if (myViewHolder.currentbid < myViewHolder.lastbid)
{
myViewHolder.sellmax.setTextColor(Color.RED);
}
myViewHolder.lastbid = myViewHolder.currentbid;
myViewHolder.lastask = myViewHolder.currentask;
}
});
}
I suggest you take a look at those classes from the Android SDK:
DiffUtil
AsyncListDiffer
ItemAnimator
DiffUtil
DiffUtil is designed to compare existing and new recycler view items and fires appropriate events. You need to pass a callback that can tell if two items are the same and if their content has changed.
AsyncListDiffer
It wraps the DiffUtil and executes it's logic asynchronously, giving better performance.
ItemAnimator
The ItemAnimator for a given RecyclerView is called by default when change events are fired on it's items. You can provide an implementation of the animateChange method to change your color accordingly.
For Future reference I have resolved the above mentioned issue using the below code.
Defined two ArrayList of String in Adapter
public class QuoteAdapter extends RecyclerView.Adapter <QuoteAdapter.MyViewHolder>{
Context context;
List<QuoteData> data;
List<String> olddatabid = new ArrayList<String>();
List<String> newdatabid = new ArrayList<String>();
List<String> olddataask = new ArrayList<String>();
List<String> newdataask = new ArrayList<String>();
class MyViewHolder extends RecyclerView.ViewHolder{
TextView time,symbol,sellmax,selllow,buymax,buylow,buy,sell,spread,lowtext,hightext;
LinearLayout layout,layoutbid,layoutask;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
time = itemView.findViewById(R.id.TVTime);
symbol = itemView.findViewById(R.id.TVSymbol);
sellmax = itemView.findViewById(R.id.TVSELLMAX);
selllow = itemView.findViewById(R.id.TVSELLLOW);
buymax = itemView.findViewById(R.id.TVBUYMAX);
buylow = itemView.findViewById(R.id.TVBUYHIGH);
buy = itemView.findViewById(R.id.TVBUY);
sell = itemView.findViewById(R.id.TVSELL);
spread = itemView.findViewById(R.id.TVSpread1);
lowtext = itemView.findViewById(R.id.low);
hightext = itemView.findViewById(R.id.high);
layout = itemView.findViewById(R.id.layout);
layoutbid = itemView.findViewById(R.id.LLBid);
layoutask = itemView.findViewById(R.id.LLAsk);
}
}
public QuoteAdapter(Context context,List<QuoteData> data)
{
this.data = data;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.quotelist,viewGroup,false);
return new MyViewHolder(view);
}
#SuppressLint("ResourceAsColor")
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, final int i) {
final QuoteData data1 = data.get(i);
myViewHolder.time.setText(data1.dLut);
myViewHolder.symbol.setText(data1.dSymbol);
myViewHolder.sellmax.setText(data1.dBid); //Bid
myViewHolder.selllow.setText(data1.dLow);
myViewHolder.buymax.setText(data1.dAsk); //ask
myViewHolder.buylow.setText(data1.dHigh);
if (newdatabid.size()< data.size())
{
newdatabid.add(data1.dBid); //Insert Value in array for the first time
}
if (olddatabid.size()< data.size())
{
olddatabid.add(data1.dBid); //Insert Value in array for the first time
}
if (newdataask.size()< data.size())
{
newdataask.add(data1.dAsk); //Insert Value in array for the first time
}
if (olddataask.size()< data.size()) //Insert Value in array for the first time
{
olddataask.add(data1.dAsk);
}
newdatabid.set(i,data1.dBid); //Store Value in array
newdataask.set(i,data1.dAsk); //Store Value in array
//Compare and perform Logic
if (Float.valueOf(newdatabid.get(i)) > Float.valueOf(olddatabid.get(i)))
{
myViewHolder.sellmax.setTextColor(Color.BLUE);
}
if (Float.valueOf(newdatabid.get(i)) < Float.valueOf(olddatabid.get(i)))
{
myViewHolder.sellmax.setTextColor(Color.RED);
}
if (Float.valueOf(newdataask.get(i)) > Float.valueOf(olddataask.get(i)))
{
myViewHolder.buymax.setTextColor(Color.BLUE);
}
if (Float.valueOf(newdataask.get(i)) < Float.valueOf(olddataask.get(i)))
{
myViewHolder.buymax.setTextColor(Color.RED);
}
olddatabid.set(i,newdatabid.get(i));
olddataask.set(i,newdataask.get(i));
}
});
}
}
#Override
public int getItemCount() {
return data.size();
}
}
I am new to android..and my question is:
I am making one android Application in which I have one RadioGroup with two Radiobutton
btnA and btnB along with some other Parameters.
if btnA is Checked than value in database is 1 and if btnB is selected then Value in Database is 0.
I am retrieving Data from database while showing My Listview.
Now My Question is I want to display Listview with listItem like :
imgA if Value From Database is 1 .
imgB if Value from Database is 0.
How to do it???
I tried this
private Integer[] Images = {R.drawable.imgA,R.drawable.imgB};
Cursor cur = dop.getData();
if(cur!= null && cur.getCount()>0)
{
if(cur.moveToFirst()){
do {Integer btnType= cur.getInt(cur.getColumnIndex(databaseName.TableName.ColumnName));
if(btnType== 1){ImageId = Images[0];}
else if(btnType== 0){ImageId= Images[1];}}
//other Params
}while (cur.moveToNext());
}
Adapter myAdp = new Adapter(Activity.this,ImageId,para);
myList.setAdapter(myAdp);
My Adapter is Like
public class Adapter extends BaseAdapter {
public Context context;
public ArrayList<String>Param1;
public int ImageId;
public Adapter(Context context,int ImageId,ArrayList<String>Param1)
{
this.context = context;
this.ImageId = ImageId;
this.Param1= Param1;
}
public int getCount(){return param1.size();}
public Object getItem(int Position){return null;}
public long getItemId(int Position){return 0;}
public class viewHolder{
TextView tvParam1;
ImageView imgType;
}
#Override
public View getView(int Position,View Child,ViewGroup Parent)
{
viewHolder vHolder;
LayoutInflater inflator;
if(Child == null)
{
inflator = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Child = inflator.inflate(R.layout.list_row,null);
vHolder = new viewHolder();
vHolder.tvparam1 = (TextView)Child.findViewById(R.id.txtParam1);
vHolder.imgType = (ImageView)Child.findViewById(R.id.imgType);
Child.setTag(vHolder);
}
else {vHolder = (viewHolder)Child.getTag();}
vHolder.tvParam1.setText(Param1.get(Position));
vHolder.imgType.setImageResource(ImageId);
return Child;
}
}
my Problem is I am getting same image for all list items.
but I want ImgA for btnA and imgB for btnB.
How to resolve this???
I got solution for this issue
what i done is: I took Integer Arraylist for storing my Images
In my Main Activity:
public int[] Images = {R.drawable.imgA,R.drawable.imgB};
public ArrayList<Integer>ImageId = new ArrayList<Integer>();
int i = 0;
if(cur.moveToFirst()){
if(btnType == 1)
{
ImageId.add(Images[0]);
}
else if(btnType == 0)
{
ImageId.add(Images[1]);
}
} while(cur.moveToNext());
also in myAdapter: I jst changed Integer Array to Integer Arraylist for Image
this solve my Problem
Take an array of ImageId and save the id in that array in specific positions.
int i = 0;
if(cur.moveToFirst()){
do {Integer btnType= cur.getInt(cur.getColumnIndex(databaseName.TableName.ColumnName));
if(btnType== 1){ImageId[i] = Images[0];}
else if(btnType== 0){ImageId[i] = Images[1];}}
i++;
} while(cur.moveToNext());
Now inside your adapter load the images like this
vHolder.imgType.setImageResource(ImageId[Position]);
You've logical error in your code.
I have a RecyclerView. In it, the items have a standard layout - one TextView and one ProgressBar.
Items are added to the recyclerview at runtime.
Whenever an Item is added, an AsyncTask is started which updates the ProgressBar.
The AsynTask holds a reference to the ProgressBar object from the RecyclerView Adapter.
The problem occurs when there are too many items in the recycler view.
I know the RecyclerView recycles any old views and thus want a way around that atleast for the progressbars.
What would be the ideal way to implement this?
Following is an excerpt from the Adapter
public class RecViewAdapter extends
RecyclerView.Adapter<RecViewAdapter.ViewHolder> {
Context mContext;
List<String> mRunns;
static ExecutorService mExec;
static HashSet<Integer> mProcessed = new HashSet<>();
public RecViewAdapter(Context context, List<String> runns) {
mContext = context;
mRunns = runns;
mExec = Executors.newFixedThreadPool(1);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.runnabel_item, viewGroup,
false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.runnName.setText(mRunns.get(position));
if (!mProcessed.contains(position)) {
new ProgressTask(holder.pBar, position).executeOnExecutor(mExec, null);
mProcessed.add(position);
}
}
#Override
public int getItemCount() {
return mRunns.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView runnName;
ProgressBar pBar;
public ViewHolder(View view) {
super(view);
runnName = (TextView) view.findViewById(R.id.textView);
pBar = (ProgressBar) view.findViewById(R.id.progressBar);
pBar.setIndeterminate(false);
pBar.setMax(100);
pBar.setProgress(0);
}
}
}
Also, I'm adding items to the RecyclerView using notifydatasetchanged.
You can simply use "findViewHolderForAdapterPosition" method of recycler view and you will get a viewHolder object from that then typecast that viewHolder into your adapter viewholder, so you can directly access your viewholder's views, ( in this case we access progressBar)
following is the sample code for kotlin
/**
* update progress bar in recycler view
* get viewHolder from position and progress bar from that viewHolder
* we are rapidly updating progressbar so we did't use notify method as it always update whole row instead of only progress bar
* #param position : position of list cell
* #param progress : new progress value
*/
private fun updateDownloadProgressBar(position :Int, progress:Int)
{
val viewHolder = recyclerViewDownload.findViewHolderForAdapterPosition(position)
(viewHolder as ViewHolderDownload).progressBarDownload.progress=progress
}
A little late, but I found a way to get it working.
My recyclerview contains a large number viewholders and only one of the viewholders have a progress bar. I have an sqlite database in which I maintain identifiers which I use to sync between my service and activity (to identify which views in the recyclerview need updating).
Depending on your implementation, you will have to find a way to identify which broadcast event corresponds to which adapter item. I have given a simplified version of what I have done below.
Model for Progress Bar:
class ProgressModel{
String progressId;
int progress = 0;
}
public int getProgress(){
return progress;
}
ViewHolder:
public class ProgressViewHolder extends RecyclerView.ViewHolder {
private ProgressBar mProgressBar;
public ProgressViewHolder(View itemView) {
mProgressBar = (ProgressBar) itemView.findViewById(R.id.mProgressBar);
}
public ProgressBar getProgressBar() {
return mProgressBar;
}
}
In the recyclerview adapter,
#Override
public void onBindViewHolder(ProgressViewHolder holder, int position) {
ProgressModel item = mData.get(position);
int progress = item.getProgress();
if (progress > 0) {
ProgressBar downloadProgress = holder.getProgressBar();
if (downloadProgress.isIndeterminate()) {
downloadProgress.setIndeterminate(false);
}
downloadProgress.setProgress(progress);
}
}
public void refresh(position, ProgressModel item){
mData.set(position, item);
notifyItemChanged(position);
}
In the Activity which implements populates the view, create a static instance of itself and pass it to the BroadcastReceiver. It took me quite a while to figure out that the static instance is required, otherwise the view doesn't get changed even though I call notifyItemchanged().
public class MainActivity extends Activity{
private static MainActivity instance;
private MyReceiver mReceiver;
private MyAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
mReceiver = new MyReceiver(instance);
//TODO: Initialize adapter with data and set to recyclerview
}
public void update(Intent intent){
ProgressModel model = new ProgressModel ();
//TODO: set progress detail from intent to model and get position from progressId
instance.mAdapter.refresh(position,model);
}
private static class MyReceiver extends BroadcastReceiver {
MainActivity activity;
public DownloadReceiver(MainActivity activity) {
this.activity = activity;
}
#Override
public void onReceive(Context context, Intent intent) {
//pass intent with progress details to activity
activity.update(intent);
}
}
}
Hope this helps.
Those who are new to Android dev and only know java might get confused of above codes given by #Mayank Sharma Which works excellent, I am giving the java version of his answer:
void updateProgress(int position, int progress)
{
MyAdapter.MyHolder mHolder =
(MyAdapter.MyHolder) myRecyclerView.findViewHolderForAdapterPosition(position);
mHolder.customProgress.setProgress(progress);
}
create this above method inside your activity where you have recycler view that contains progress bar and call this method whenever you want some changes in your progress bar.