I have the following code for the recyclerview adapter for an android app that I'm working on right now:
#Override
public void onBindViewHolder(final FeedViewHolder contactViewHolder, final int i) {
final FeedInfo ci = feedInfoList.get(i);
//Set the text of the feed with your data
contactViewHolder.feedText.setText(ci.getFeed());
contactViewHolder.surNameText.setText(ci.getSurName());
contactViewHolder.nameText.setText(ci.getFirstName());
contactViewHolder.feedDate.setText(ci.getDate());
contactViewHolder.numberOfGoingText.setText(ci.getNumber_of_going());
contactViewHolder.numberOfInterestedText.setText(ci.getNumber_of_interested());
//seteaza fotografia de profil in postare
new ProfilePictureDownloadImage(contactViewHolder.profilePicture).execute(ci.getProfileImageURL());
ImageButton interestedButton = contactViewHolder.interestedButton;
interestedButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = i;
FeedInfo fi = feedInfoList.get(position);
int displayedNumberOfInterested = Integer.parseInt(ci.getNumber_of_interested()) + 1;
contactViewHolder.numberOfInterestedText.setText(Integer.toString(displayedNumberOfInterested));
System.out.println("emilutzy interested from within" + fi.getPostID());
contactViewHolder.surNameText.setText("kk");
}
});
}
The problem is the click listener. In theory the button I press should increment the number right next to it. However, since I have to declare onBindViewHolder's arguments as final, only the first click works, the rest of the clicks do not change the value of the number. I am new to Android, so could you please help me find a better solution?
There's a nice method called getAdapterPosition() that you can use in your RecyclerView's ViewHolder.
Instead of setting the click listener in onBindViewHolder, set it in the constructor of your ViewHolder like so:
public class FeedViewHolder extends RecyclerView.ViewHolder {
private TextView feedText;
private TextView surNameText;
private Button interestedButton;
// ... the rest of your viewholder elements
public FeedViewHolder(View itemView) {
super(itemView);
feedtext = itemView.findViewById(R.id.feedtext);
// ... find your other views
interestedButton.setOnClickListener(new View.OnClickListener() {
final FeedInfo fi = feedInfoList.get(getAdapterPosition());
int numInterested = Integer.parseInt(ci.getNumber_of_interested()) + 1;
// setting the views here might work,
// but you will find that they reset themselves
// after you scroll up and down (views get recycled).
// find a way to update feedInfoList,
// I like to use EventBus to send an event to the
// host activity/fragment like so:
EventBus.getDefault().post(
new UpdateFeedInfoListEvent(getAdapterPosition(), numInterested));
// in your host activity/fragment,
// update the list and call
// notifyDatasetChanged/notifyDataUpdated()
//on this RecyclerView adapter accordingly
});
}
}
Don't set your position in onBindViewHolder to final (Android Studio will warn you why).
I'm not sure how the object FeedInfo looks like but you could also at a method called for example increaseNumberOfInterested() which would increase the value of Number_of_interested by one and would persist in the object when the recyclerview recycle the cell. it would like kind of like below
#Override
public void onBindViewHolder(final FeedViewHolder contactViewHolder, final int i) {
final FeedInfo ci = feedInfoList.get(i);
//Set the text of the feed with your data
contactViewHolder.numberOfInterestedText.setText(ci.getNumber_of_interested());
contactViewHolder.interestedButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Increase the number of interested in the object, so it can be persisted when cell is reclycled
ci.setNumberOfInterested(ci.getNumber_of_interested()) + 1);
//Get new value and display
contactViewHolder.numberOfInterestedText.setText(Integer.toString(ci.getNumber_of_interested()));
}
Related
I tried to get a array from an adapter from my code
here is the array that i wanted to get from my adapter, named MakananAdapter :
private int[] JumlahPesan = {0,0,0,0};
The array is changing constantly since user will be deciding the amount that they want, here is the onBindViewHolder code:
public void onBindViewHolder(#NonNull viewHolder holder, final int position) {
ImageView ivMakanan = holder.ivMakanan;
TextView tvNamaHarga = holder.tvNamaMakanan;
TextView tvKetersediaan = holder.tvKetersediaan;
TextView tvHarga = holder.tvHargaMakanan;
final TextView tvPesanan = holder.tvJumlahPesanan;
Button btnTambah = holder.btnTambah;
Button btnKurang = holder.btnKurang;
ivMakanan.setImageResource(makanans.get(position).getGambarMakanan());
tvNamaHarga.setText(makanans.get(position).getNamaMakanan());
tvKetersediaan.setText("Stok : " + makanans.get(position).getStatusMakanan());
tvHarga.setText("Harga : " + makanans.get(position).getHargaMakanan());
btnTambah.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
JumlahPesan[position]++;
tvPesanan.setText(String.valueOf(JumlahPesan[position]));
}
});
btnKurang.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
JumlahPesan[position]--;
tvPesanan.setText(String.valueOf(JumlahPesan[position]));
}
});
}
as you can see i make a button that increase and decrease the data of the array
and i tried to get the array data to my activity, but i still get error.
my activity named PilihMakananActivity.class
here is the array to save the data from the adapter
private int[] Pesanan = {0,0,0,0};
and i tried to get the data in onResume
protected void onResume() {
super.onResume();
com.example.iotforcanteen.adapter.MakananAdapter coba = null;
for (int i = 0; i<4 ; i++) {
Pesanan [i]= coba.AmbilJumlahPesanan(i);
}
}
and i tried to show it in a snackbar like this
btnKonfirmasi.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Snackbar.make(v, Pesanan[0] + Pesanan[1] + Pesanan[2] + Pesanan[3],Snackbar.LENGTH_SHORT).show();
}
});
Im so sorry if the code is so messy, because im new to android development.so is there any way to fix this error?
It has error because you set to your adapter null value in onResume .
But in general I assume you use RecyclerView in code so the steps for using RecyclerView is important, first you must set LayoutManager for RecyclerView. Then make an adapter and set it to RecyclerView and I recommend you to do this steps in onCreate not onResume.
Here is a little example
RecyclerView recyclerView = findViewById(R.id.rec);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new MyRecyclerViewAdapter(this, list);
recyclerView.setAdapter(adapter);
Also make your array in adapter public or write getter for it. After calling setAdapter for RecyclerView, you can get your data in adapter. For example you can define a Button and in OnClickListener get the desired array(here is JumlahPesan) in adapter
you aren't initiating this adapter (null) and few lines below trying to access data from it, this is NullPointerException
com.example.iotforcanteen.adapter.MakananAdapter coba = null;
for (int i = 0; i<4 ; i++) {
Pesanan [i]= coba.AmbilJumlahPesanan(i);
}
make reference to already exitsting adapter atached to your ListView or RecyclerView, not freshly created and not initialised at all
note that onResume is called once at the beggining, thus your Pesanan won't have current data, only copy from start of Activity
maybe just get your values straightly when button pressed, without a copy of array in Activity:
btnKonfirmasi.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Snackbar.make(coordinatorLayout,
adapterAttachedToView.AmbilJumlahPesanan(0) + " " +
adapterAttachedToView.AmbilJumlahPesanan(1) + " " +
adapterAttachedToView.AmbilJumlahPesanan(2) + " " +
adapterAttachedToView.AmbilJumlahPesanan(3),
Snackbar.LENGTH_SHORT).show();
}
});
note that Snackbar.make( should take View, in which Snackbar will appear, not clicked Button (you are passing v to Snackbar.make)
I am working on a Quiz app. First when a user opens the app they go to the MainActivity, from there when they press start they go to the Categories Activity , from there after selecting a category they go to the Sets Activity, from there after selecting a set the go to the Questions Activity and finally after completing all the questions they reach the Score Activity. Here in the score activity when the click on Done button they are redirected to the MainActivity. In the Score Activity i want to change the color of the Set that they completed to green instead of the default color. How can i do this? I created a sets item layout xml file and used an adapter to fill the gridview in the Sets Activity with views from the adapter. Currently i am getting a null object reference after clicking the Done button in the ScoreActivity.
Here is the code :
SetsAdapter.java
public class SetsAdapter extends BaseAdapter {
private int numOfSets;
public SetsAdapter(int numOfSets) {
this.numOfSets = numOfSets;
}
#Override
public int getCount() {
return numOfSets;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
View view;
if(convertView == null){
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.set_item_layout, parent, false);
}
else {
view = convertView;
}
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent questionIntent = new Intent(parent.getContext(), QuestionActivity.class);
questionIntent.putExtra("SETNUM", position +1);
parent.getContext().startActivity(questionIntent);
}
});
((TextView) view.findViewById(R.id.setNumber)).setText(String.valueOf(position+1));
return view;
}
}
SetsActivity.java
public class SetsActivity extends AppCompatActivity {
private GridView sets_grid;
private FirebaseFirestore firestore;
public static int categoryID;
private Dialog loadingDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sets);
Toolbar toolbar = (Toolbar)findViewById(R.id.set_toolbar);
setSupportActionBar(toolbar);
String title = getIntent().getStringExtra("CATEGORY");
categoryID = getIntent().getIntExtra("CATEGORY_ID",1);
getSupportActionBar().setTitle(title);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
sets_grid = findViewById(R.id.sets_gridView);
loadingDialog = new Dialog(SetsActivity.this);
loadingDialog.setContentView(R.layout.loading_progressbar);
loadingDialog.setCancelable(false);
loadingDialog.getWindow().setBackgroundDrawableResource(R.drawable.progress_background);
loadingDialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
loadingDialog.show();
firestore = FirebaseFirestore.getInstance();
loadSets();
}
private void loadSets() {
firestore.collection("Quiz").document("CAT" + String.valueOf(categoryID))
.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot doc = task.getResult();
if (doc.exists()) {
long sets = (long) doc.get("SETS");
SetsAdapter adapter = new SetsAdapter(Integer.valueOf((int)sets));
sets_grid.setAdapter(adapter);
} else {
Toast.makeText(SetsActivity.this, "No Sets Exists!", Toast.LENGTH_SHORT).show();
finish();
}
} else {
Toast.makeText(SetsActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
loadingDialog.cancel();
}
});
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
if(item.getItemId() == android.R.id.home)
finish();
return super.onOptionsItemSelected(item);
}
}
ScoreActivity.java
public class ScoreActivity extends AppCompatActivity {
private TextView score;
private Button done;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_score);
score = findViewById(R.id.score_tv);
done = findViewById(R.id.score_activity_done);
String score_str = getIntent().getStringExtra("SCORE");
final int setNum = getIntent().getIntExtra("SetNum", 1);
score.setText(score_str);
done.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Here is the issue I am facing
View view = findViewById(R.id.setNumber);
view.setBackgroundColor(Color.GREEN);
Intent mainIntent = new Intent(ScoreActivity.this, MainActivity.class);
startActivity(mainIntent);
ScoreActivity.this.finish();
}
});
}
}
As your activity Sequence is MainActivity -> Categories -> Sets -> Scores.
You've two options to change the color with two different life cycle of the change.
To change the color on a temporary basis, this will reset itself after closing the app or resrtating the 'Sets' activity. It can be done in two ways: Using Public Static Variable and using a public function.
To change the color on a permanent basis until the app is uninstalled/reinstalled. You should use SharedPreferences. SharedPreferences acts like a private data stored in device's memory for further use and it stays there unchanged until and unless the app is removed/data is cleared. Although, apps with root permission can access any app's SharedPreferences data and can modify it as well.You can use SharedPreferences as explained here. Or, you can use some library to access it an easy way. The way I use it in all my apps is TinyDB(it's just a java/kotlin file). This works as:
//store the value from ScoreActivity after completion as
TinyDB tinyDB = TinyDB(this);
tinyDB.putBoolean("isSet1Completed",true);
//access the boolean variable in SetsActivity to change the color of any set that
//is completed and if it's true, just change the color.
TinyDB tinyDB = TinyDB(this);
Boolean bool1 = tinyDB.getBoolean("isSet1Completed");
But, it's your choice what way you want to prefer.
Now, this was about the lifecycle of the change you'll do: Temp or Permanent. Now, we'll talk about how you change the color.
Using public static variable in Sets activity. What you can do is you can set the imageView/textview whose background you want to change as public static variable. Remember, this idea is not preferred as it causes memory leak but it's just easy.
Declare it as public static ImageView imageview;(or TextView) intialize it in the
onCreated() as imageView = finViewById(R.id.viewId); in Sets activity. Call
it as new SetsActivity().imageView.setBackgroundColor(yourColor); in ScoreActivity.
Second way is to create a public function in SetsAcitvity, putting the color change code in it, and then calling it from the ScoreActivity. Just declare it as public void changeColor(){ //your work} and call it from ScoreActivity as new SetsActivity().changeCOlor(). You can also pass some arguments to the function like setId.
I've provided you every thing you need. Rest you should figure out yourself to actually learn it and not copy it.
I think simply you add flag in MainActivity.
for example, add flag in MainActivity.
boolean isFromDone = false;
and when done clicked,
done.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Here is the issue I am facing
Intent mainIntent = new Intent(ScoreActivity.this, MainActivity.class);
mainIntent.putExtra("FromDone", true);
startActivity(mainIntent);
ScoreActivity.this.finish();
}
});
and in MainActivity, add this.
#Override
protected void onResume() {
super.onResume();
isFromDone = getIntent().getBooleanExtra("FromDone", false);
if(isFromDone) {
(TextView) view.findViewById(R.id.setNumber)).setBackgroundColor(Color.GREEN);
}
}
Suppose you have a Linear Layout in Activity A and you want to change it's background color from a button click which is present in Activity B.
Step 1 Create a class and declare a static variable.
class Util { private static LinearLayout mylayout ; }
Step 2
In the activity which is holding this layout, initialize it.
Util.mylayout = findviewbyid(R.id.linear);
Step 3Change the background color on button click from Activity B
onClick{
Util.mylayout.setBackgroundColor(Color.RED);
}
I have the following which lets the user click on an image and it turns the image invisible:
holder.redView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ManualPlayingChange.notToPlaying(view);
}
});
public static void notToPlaying(View a) {
if(a.isShown()) {
a.setVisibility(View.INVISIBLE);
// b.setVisibility(View.VISIBLE);
}
}
What I want to do, is pass through another View, so that a different Ciew also changes on the same click.
The views I have:
public ViewHolder(View p) {
super(p);
playerView = p.findViewById(R.id.aPlayerInTheList);
greenView = p.findViewById(R.id.isPlaying);
amberView = p.findViewById(R.id.mightBePlaying);
redView = p.findViewById(R.id.notPlaying);
}
So I just want to be able to cycle through the 3 images.
You can just pass the holder (i.e ViewHolder) itself in notToPlaying() method instead of single view and then access all the views like:
public static void notToPlaying(ViewHolder holder){
// now you can access all the views
holder.playerView.setVisibility(View.VISIBLE);
holder.redView.setVisibility(View.GONE); // and so on.
}
I am making use of recycler view. I have a layout that is highlighted in light red,this layout is included for each item in the recycler view. The light red layout is placed over the background image. I am using setTag method to identify the clicks of the buttons in red layout. That is working properly when i click i get the position. The problem is i want to change the image at specific position.
For example : Consider the heart button. I have set a tag on it like this.
heartButton = findViewById(id);
heartButton.setTag(position);
now i get the position by using the getTag method. But now i want to change the image of the heartButton at the a specific position. Is there something like
heartButton.getTag(position).setImageResouce(drawable);
If not how do i do this then.
use setBackgroundResource(R.drawable.XXX)
http://developer.android.com/reference/android/view/View.html#setBackgroundResource(int)
Proper way to do this is,
You have to keep the state of the heart button stored in the model(POJO) which is passed to custom adapter.
e.g.
class ModelListItem{
public static final int HEART=1,BROKEN_HEART=2;
int heartButtonState;
}
Now in onClick() of heart button, get that object from adapter using position,cosidering you have already figured it out on how to preserve position from heart button
ModelListItem item = (ModelListItem)adapter.getItem(position)
Change the state of heart button;
item.setHeartButtonState(ModelListItem.BROKEN_HEART);
adapter.notifyDatasetChanged();
You already know below explaination but just in case
To work this properly,in your getView methode of adapter you need to put the check on heartButtonState(); and use appropriate image resource.
getView(BOILERPLATE){
BOILERPLATE
switch(item.getheartButtonState()){
case ModelItemList.HEART:
heartbutton.setImageResource(heart_image);
break;
case ModelItemList.BROKEN_HEART:
heartbutton.setImageResource(broken_heart_image);
break;
}
I made a custom click listener and updated the like in the setter getter.But this works only when the view has been moved out of the view (i think it is the scrapeview)
The Setter Getter Class
public class DemoData {
int background;
boolean liked;
public DemoData(int background) {
this.background = background;
}
public int getBackground() {
return background;
}
// public void setBackground(int background) {
// this.background = background;
// }
public boolean isLiked() {
return liked;
}
public void setLiked(boolean liked) {
this.liked = liked;
}
}
The onBindViewHolder function of the recycler view
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
background = (ImageView) holder.view.findViewById(R.id.image);
layout = (LinearLayout) holder.view.findViewById(R.id.layout);
delete = (ImageView) layout.findViewById(R.id.delete);
lock = (ImageView) layout.findViewById(R.id.lock);
delete.setTag("delete_"+position);
lock.setTag("lock_"+position);
if(Constants.demoDatas.get(position).isLiked()){
delete.setImageResource(R.drawable.ic_launcher);
}
else{
delete.setImageResource(android.R.drawable.ic_delete);
}
delete.setOnClickListener(new CustomClickListener(position));
lock.setOnClickListener(new CustomClickListener(position));
}
The custom click listener is as below
public class CustomClickListener implements View.OnClickListener {
int position;
public CustomClickListener(int position) {
this.position = position;
}
#Override
public void onClick(View v) {
String tag = (String) v.getTag();
String identifier[] = tag.split("_");
// this line saves my state in the Setter Getter Class
Constants.demoDatas.get(position).setLiked(true);
}
}
I am building an android app for work using android studio. I have a list of witnesses stored as sharedpreference, when the uses clicks to amend these witnesses are split up and listed the user can then click the witness they want to amend, well that's the plan, I just cant seem pass the witness listed number (basically the number used in the loop) to another function that will load the witnesses information into a form to be amended. Below is my code:
String s= WitnessDetails;
String[] array = s.split("$_$(?=[0-9])");
LinearLayout parentLayout = (LinearLayout)findViewById(R.id.LinearLayout);
// Layout inflater
LayoutInflater layoutInflater = getLayoutInflater();
View view;
for(String str : array)
{
countWitness++;
String WitnessName= str;
view = layoutInflater.inflate(R.layout.witnesses_activity, parentLayout, false);
// In order to get the view we have to use the new view with text_layout in it
TextView textView = (TextView)view.findViewById(R.id.Witnesses_details);
textView.setText("Witness 1: " + countWitness + "\n" + WitnessName+"\n Edit");
// Add the text view to the parent layout
parentLayout.addView(textView);
textView.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
Amendwitness(countWitness);
}
});
}
public void Amendwitness(witnessNum){
}
You are using countWitness inside an inner anonymous class, so either
countWitness should be final
or
countWitness should be class field
otherwise compiler will show error. Since countWitness is incremented inside for-loop, it cannot be declared final, so the only solution left is to make it as class-field
OR
you can use the setTag(Object) function
for(String str : array)
{
countWitness++;
String WitnessName= str;
view = layoutInflater.inflate(R.layout.witnesses_activity, parentLayout, false);
TextView textView = (TextView)view.findViewById(R.id.Witnesses_details);
textView.setText("Witness 1: " + countWitness + "\n" + WitnessName+"\n Edit");
textView.setTag(countWitness); // set the countWitness as tag object
parentLayout.addView(textView);
textView.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
Amendwitness((Integer)arg0.getTag());
}
});
}
Here are two possibilities:
Add a custom MyClickListener class:
public class MyClickListener implements View.OnClickListener {
private int mIndex;
public MyClickListener(int index) {
mIndex = index;
}
#Override
public void onClick(View v) {
Amendwitness(index);
}
}
Add your OnClickListeners like this:
textView.setOnClickListener(new MyClickListener(countWitness));
Use the tag of the View:
textView.setTag(countWitness);
textView.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
Amendwitness(v.getTag());
}
});