So I am trying to create a challenge game where you can have 2 options to pick from, I have 2 card views setup in a recycler view setup, how would I change the values in them to get a new challenge with the click of a button from the main activity if a user wanted a new challenge?
I am fetching the data from each battle at random using an SQLite database.
My onBindViewHolder
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
SQLiteDatabase sqLiteDatabase = faceMashDatabaseHelper.getReadableDatabase();
cursor = sqLiteDatabase.query("images", new String[]{"id","filename","name"},null,null,null,null,null);
MyViewHolder myViewHolder = (MyViewHolder) holder;
int position1 = (int)(Math.random()*(faceMashDatabaseHelper.getCount()-0+1)+0);
cursor.moveToPosition(position1);
String filename = cursor.getString(cursor.getColumnIndex("filename"));
String name = cursor.getString(cursor.getColumnIndex("name"));
CardView cardView = myViewHolder.getCardView();
ImageView imageView = (ImageView) cardView.findViewById(R.id.imageView);
TextView nameView = (TextView) cardView.findViewById(R.id.nameView);
nameView.setText(name);
File file = context.getFileStreamPath(filename);
if(file.exists()){
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
imageView.setImageBitmap(bitmap);
}
cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = nameView.getText().toString();
listener.onClick(position,name);
}
}); //This on click is for the card views themselves.
}
My onClick at the current moment, I'm using a toolbar option
case R.id.newbattle:
faceMashAdapter.notifyDataSetChanged();
First of all, you should never do heavy work in onBindViewHolder since it will be called every time your View is recreated after it was recycled (because it was unused).
You should query your Database outside of the Adapter instead and occupy a List with your queried data. Your List should have as many entries as your RecyclerView has entries. Then you can use the position parameter in onBindViewHolder to always get the correct data for the entry (list.get(position)).
This way you can just change the content of your List and notify the Adapter which items have changed via notifyItemChanged (or if all items should be refreshed, use notifyDataSetChanged). That way the content of the Adapter is refreshed.
So what you should do in your onClick is update the content of your List and notify the Adapter.
Related
With RecyclerView, the off-screen EditTexts will be recycled. For example, if we have a list of 200 items, and it shows 2 items at one time, we will only ever have 2 EditText. They will reuse the higher EditText for the lower elements.
For example, here is a list that contains EditText showing only 2 at a time, and as the user scrolls, it will recycle and reuse them.
EditText A
Edittext B
EditText C (recycled)
EditText D (recycled)
....
This means we cannot just loop over all the elements later and get the values, as they don't store their values.
So, what i am asking is that is there anyway to loop over all the items and get there values even the recycled ones and save them to firebase
EDIT:
I have a recyclerview with an edit text inside it the user enter numbers inside edit text and when he click save button a for loop iterate over all the recyclerview edit text and save data to Firebase
this is my fragment class
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.purchases_list_activity, container, false);
mItem = new ArrayList<>();
mAdapter = new PurchasesAdapter(getContext(), mItem, this);
re = view.findViewById(R.id.itemsPListView);
re.setLayoutManager(new LinearLayoutManager(getActivity()));
re.setAdapter(mAdapter);
fab = view.findViewById(R.id.fab_purchases);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < mItem.size(); i++) {
final ItemsAdapter mylist = mItem.get(i);
View childView = re.getChildAt(i);
final EditText Qty = childView.findViewById(R.id.itemNewPcs);
final String qty = Qty.getText().toString();
Toast.makeText(getContext(),qty+"",Toast.LENGTH_LONG).show();
..
}
I think that what you might need is the RecyclerView's OnChildAttachStateChangeListener functionality. Basically, you implement your own class that implements the OnChildAttachStateChangeListener interface. Specifically, the implement the onChildViewAttachedToWindow method to do save to Firebase or add to a list that can be added to Firebase later - basically whatever you need. You just attach your custom listener via the addOnChildAttachStateChangeListener() method.
Hope that helps.
I'm new to android, but have a good JavaFX experience. I'm trying to create a custom view that i can reuse, but having a hard time figuring out the correct way to do it.
In javafx i could achieve this by: Creating a separate fxml file defining the layout of the custom view, then create a controller class linked to the fxml file, in that class, i'd have a method to retrieve the data model of the controller and use it to fill in the labels, etc.
The custom view i want would be
Constrained Layout
TextView (constrained to right anchor)
Round TextView (constrained to left anchor)
What is the best way to do this in android? Also, Is it possible to achieve this with a RecyclerView? If yes, how can i use a custom view for each item and set its data?
The question is broad. You may need additional research on creating views
Create a recyclerview in the main.xml,
a separate file with an item view.
You have 3 views in your item view - white background with margins (linearlayout?), right textView, and left textview.
The left textview should have android:background="drawable/round_shape" and round_shape.xml defined in your drawables folder. Everything is done in 3 xml files, main.xml for recyclerview, item.xml, round_background.xml. Then, the recyclerview adapter to bind the textviews with your array, and recyclerview initialization
A typical RV adaptor
public class MyRV extends RecyclerView.Adapter<MyRV.ViewHolder> {
private List<MyModelItemWith2Strings> mDataSet; // You may need to setup an array,
// with 2 String objects - for the right and left textviews
// Use an array of class with 2 elements rather than <String>, e.g. List<MyModelItemWith2Strings>
// pass your model here
// this setData will be used to provide the contents for the textviews
void setData(List< /* set your 2 string class here*/ > dataSet) {
mDataSet = dataSet;
}
static class ViewHolder extends RecyclerView.ViewHolder {
// Here you bind item TV's
// first you declare textviews that you will use to fill with data
// Add any other item views you will need to fill in
public TextView tv;
public TextView tv2;
public ViewHolder(LinearLayout v) {
super(v);
// Bind itemview views here. Put R.id.tv from your itemview.xml
tv = v.findViewById(R.id.....);
tv2 = v...
}
}
// Add your itemview layout here
#Override
public MyRV.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(/***R.layout.item_view***/, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder( MyRV.ViewHolder h, int position) {
// get content from your model (the above list) and fill in the the itemview textviews
String a= mDataSet.get(position).getItem1();
String b = mDataSet.get(position). getItem2();
...
h.tv.setText(a);
// set clickers if you want to. The clicker class is below.
h.tv.setOnClickListener(new Click(position));
h.tv2.setText(...)
}
// This is obligatory to pass for your RV to initialize. It won't work if you don' t tell Android how to count your array soze
#Override
public int getItemCount() {
return mDataSet.size();
}
// These are my implementation of clickers. I prefer to put them in the nested class of the adapter.
private class Click implements OnClickListener {
private int pos;
Click(int position) {
pos = position;
}
#Override
public void onClick(View p1) {
// get data from your array on click
mDataSet.get(pos);
// Use pos as position on the array, mData.get(pos)
}
}
}
Then, in your main class set a recyclerview
RecyclerView rv = (RecyclerView) findViewById(R.id.rv_In_Main_Xml);
// just additional tunings.
rv.setHasFixedSize(true);
rv.setLayoutManager(new LinearLayoutManager(context)); // <- context = this, if you are in the Main activity
Then set the adapter
MyRV rva = new MyRV();
rva.setData(myArray_with_2_string_objects_to_fill_tvs);
rv.setAdaptor(rva);
And your recycler view gets filled with data
I am using the FirebaseRecyclerAdapter to populate views dynamically as data is added to Firebase.
However, I also want to modify the views that the RecyclerView populates as data changes in Firebase. For instance, I have a counter on each view that is populated, and as people vote on that topic, I want the counter to increase:
My idea is to set a tag to each view that is populated in the RecyclerView, find a way to get the current views that are on the screen, and then update those views dynamically. I am kind of lost how I would proceed, but here is my PollHolder:
public static class PollHolder extends RecyclerView.ViewHolder {
TextView mPollQuestion;
TextView mVoteCount;
ImageView mPollImage;
View mView;
String mTag;
public PollHolder(View itemView) {
super(itemView);
mPollQuestion = (TextView) itemView.findViewById(R.id.latest_item_question);
mPollImage = (ImageView) itemView.findViewById(R.id.pollThumbNailImage);
mVoteCount = (TextView) itemView.findViewById(R.id.latest_item_poll_count);
this.mView = itemView;
}
public void setVoteCount (String voteCount){
mVoteCount.setText(voteCount);
}
public void setTag(String tag){
this.mTag = tag;
}
public View getViewByTag(String tag){
return mView;
}
}
Here are the views in the RecyclerView. I want the counter in the lower right corner to update, but again I do not want to completely recreate/repopulate the RecyclerView.
when you insert new get the position of that row.Then you can refresh specific row by using
notifyItemInserted (int position);
Its not refresh or recreate all data in RecyclerView but its alternate the count of list and refresh the particular position.
Representations of other existing items in the data set are still considered up to date and will not be rebound, though their positions may be altered.
More details refer RecyclerView.Adapter
Is it possible to have methods and actual coding inside each item for a GridView?
The app that I am attempting to create is a currency converter, and I am currently displaying 3 images in the gridView: Euros, Pesos, and Rupees.
Once the user clicks on one, I want the open to open up a new XML which displays a textView. The user enters the value of US dollars in the textView and clicks the compute button. The app then displays the converted amount in the bottom of the screen.
The problem is that I am unable to figure out how to open up a new XML every time a picture is clicked on in the gridView. Assuming that I am able to do this, I am also unsure of where to place the code that goes behind the conversions. Would I make a new .java or just place is all in MainActivity.java?
Thanks.
What you might be best doing is when the user clicks on a currency it takes them to another activity where you would then load another xml for whatever you want to show.
In order to detect which item had been clicked you can implement an onItemClickListener for example
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
//this assumes you give the gridview a list of currency which it then displays. Here we get the currency selected to then pass to our new activity
String selectedCurrency = myArrayOfCurrencies.get(position);
//then start new activity and give it the currency. This means we won't have to create an activity for each currency. You just need to create 1 and then based on what currency you give it will change the functionality
Intent intent = new Intent(this, Converter.class);
Intent.putExtra("currency", selectedCurrency);
startActivity(intent);
}
First you should be able to detect the clicks on each item of the GridView by calling the setOnItemClickListener() method.
If you set the clicklistener and you still can't detect the clicks, then most probably you need to add those attribtutes to your imageView in the xml
android:focusable="false"
android:focusableInTouchMode="false"
Second, once you are able to detect the clicks you can start new activity or add fragment that contains that edit text that will promote the user to enter the value.
Third, I would suggest to put the code responsible for the currency conversion in a class separately and create static methods that takes a value and convert it to the other curreny such as:
public class CurrencyConverter {
public static double convertToRupees (String currencyType, double currencyValue){
....
return currencyInRupees;
}
}
and by the way I would suggest you to use RecyclerView with grid layout manager instead of GridView.
I would create more classes.
You asked how to open a different XML file for each gridView item.
Create a custom adapter that extends BaseAdapter.
Override getView and for each view attach the right Xml file, according to the position.
For example:
YourActivity.java:
GridView gridView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gridView = (GridView) findViewById(R.id.gridView);
gridView.setAdapter(new MyAdapter(getApplicationContext());
}
MyAdapter.java:
...
#Override
public int getCount() {
return XmlArr.length;
}
#Override
public Object getItem(int position) {
return XmlArr[position];
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Xml myXml = (Xml) getItem(position);
Holder holder;
if (convertView == null) {
// Set your view's layout. Consider using LayoutInflater.
// Use a static holder to prevent re-initialization:
holder = new Holder();
// holder.textView = ...
// holder.Xml = ...
// Or whatever you decided to have in each gridView item.
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.Xml = myXml;
...
return convertView;
}
static class Holder() {
TextView tv;
Xml xml;
...
}
I assumed you would used an Xml array (xmlArr).
Now you have option to play with each gridView item as you wish. You can set each view/button/textView an onItemClickListener, or you can also set the whole gridView an onItemClickListener (from YourActivity.java).
Hope this helps.
I am currently learning android and working on a few projects...in this one I have a list view and when I click it I pass the name of the row I click and a value from a database to my NEXT activity and that works 100%.
This is part of my main Activity on create code. where I populate the listview from my database here and other stuff.
lvUsers = (ListView)findViewById(id.lvUsers);
List<String> listUsers = dbHeplper.getAllUsers();
if(listUsers != null){
adapter = new ArrayAdapter<String>(getApplicationContext(),android.R.layout.simple_list_item_1, android.R.id.text1,listUsers);
// adapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.test, id.movie_title,listUsers);
lvUsers.setAdapter(adapter);
lvUsers.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
String product= ((TextView)view).getText().toString();
// String product= String.valueOf((TextView)findViewById(id.movie_title));
// Toast.makeText(getBaseContext(),product,Toast.LENGTH_LONG).show();
Intent x = new Intent(getApplicationContext(), SingleListItem.class);
// sending data to new activity
x.putExtra("product", product);
startActivity(x);
}
});
This code works fine, but I have attempted to style my row myself if you look at this line. (which is commented in the main view)
adapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.test, id.movie_title,listUsers);.
Adding this threw the error of relativelayout cannot be cast to android widget textview on the line
String product= ((TextView)view).getText().toString();
So I took that to mean it does not know which textview I am referring to anymore. so I edited that line to
String product= String.valueOf((TextView)findViewById(id.movie_title));
because movie_title is the ID of the textview in the test layout.
The then disappeared but now when I click the row instead of getting that data I expected like row name and data from database. the textview in my second activity is displaying
"android.widget.TextView{176f155.v.Ed (more random numbers) app:id/movie_title}
(Some of the code from second activity
Intent i=getIntent();
String name = i.getStringExtra("product");
txtName.setText(name);
Like I said all these errors are occurring from me trying to implement my owned custom rows. So If anyone could point out where I went wrong while doing this I would be grateful .
Have you tried creating a custom adapter and making it extend ArrayAdapter ? You can do this easily and then inside your getView() of your custom adapter you can inflate your textview with your appropriate text . Check this : https://stackoverflow.com/a/8166802/1497188
Also String product= String.valueOf((TextView)findViewById(id.movie_title)); , this line is getting the ID of the view and not the text inside the view. The appropriate view to do it is that from inside your onItemClick() you would convert/cast your view into a textview and then do the getText().toString() on that view. Just Search online how to create custom adapters and onItemClick Listener for Adapter
EDIT:
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
String data=(String)adapterView.getItemAtPosition(arg2);
This will atleast help you get the string from inside your onItemClick()