Setting ArrayList entries as ViewFlipper children - java

There are over 30 views to be displayed in my activity. Each one is a RelativeLayout with nested ImageView and some TextViews within (some of them are static, some of them are variable). Refactored to programmatic way of displaying results it looks like:
tbnImages = db.getTbnImages();
tbnNames = db.getTbnNames();
tbnExts = db.getTbnExts();
tbnDescs = db.getTbnDescs();
vf = (ViewFlipper) findViewById(R.id.viewFlipperTbnImages);
vf.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.left_in));
vf.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.left_out));
detector = new SimpleGestureFilter(this, this);
tbnImage = (ImageView) findViewById(R.id.tbnImage);
tbnName = (TextView) findViewById(R.id.tbnName);
tbnExt = (TextView) findViewById(R.id.tbnExt);
tbnDesc = (TextView) findViewById(R.id.tbnDesc);
How should I configure my ViewFlipper so that .showNext() and .showPrevious() methods would display proper views, including correct behaviour when launching method upon the last view (.showNext() redirects to the first view) and the first one (.showPrevious() presents the last view)?
public void onSwipe(int direction) {
switch (direction) {
case SimpleGestureFilter.SWIPE_RIGHT:
vf.showPrevious();
break;
case SimpleGestureFilter.SWIPE_LEFT:
vf.showNext();
break;
}
All variables (ImageView picture and TextView texts) are taken from an ArrayList. How to set them as ViewFlipper children?

You need to generate all the 30 views and add them as children of the ViewFlipper. If all your views share one single layout, you should create a custom view to handle it.
public class CustomView extends RelativeLayout{
public CustomView(Context context, Uri imageUri, String tbnName, String tbnDescripton, String tbnDesc) {
super(context);
LayoutInflater.from(context).inflate(R.layout.your_layout_resource, this, true);
ImageView tbnImage = (ImageView) findViewById(R.id.tbnImage);
TextView tbnName = (TextView) findViewById(R.id.tbnName);
TextView tbnExt = (TextView) findViewById(R.id.tbnExt);
TextView tbnDesc = (TextView) findViewById(R.id.tbnDesc);
tbnImage.setImageURI(imageUri);
tbnName.setText(tbnName);
tbnImage.setText(tbnDescription);
tbnExt.setText(tbnDesc);
}
}
Where 'your_layout_resource' is your current RelativeLayout resource with a 'merge' element as a root. Then you just have to create all the views you need and add them to your ViewFlipper:
for(int i = 0; i<tbnImages.size(); i++){
vf.addView(new CustomView(context,
tbnImages.get(i),
tbnNames.get(i),
tbnDescripton.get(i),
tbnDescs.get(i)));
}
Anyway, 30 views are a lot of views. Are you sure a ViewPager wouldn't work for you?

Related

change data in recycler view with click of button

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.

Registering multiple click events programatically (Android/Java)

So i have this program which create my list of cards which are relative layouts and they look like this.
Here is the code of it creation. Ps its in a for loop
LayoutInflater mInflater = (LayoutInflater) atv.getSystemService(atv.LAYOUT_INFLATER_SERVICE);
LinearLayout relativeLayoutz = (LinearLayout) mInflater.inflate(R.layout.rtl_enterprise, parent);
RelativeLayout rl = (RelativeLayout) relativeLayoutz.findViewById(R.id.rl_empresa);
rl.setId(i);
ImageView img = (ImageView) rl.getChildAt(0);
//
//Performance bottleneck needs fixing/ not loading all images and overloading thread
//
//loadImageByUrl(atv, enterprises.getEnterprises().get(k).getPhoto().toString(), img);
TextView txt = (TextView) rl.getChildAt(1);
txt.setText(enterprises.getEnterprises().get(i).getEnterpriseName());
TextView txt2 = (TextView) rl.getChildAt(2);
txt2.setText(enterprises.getEnterprises().get(i).getEnterpriseType().getEnterpriseTypeName());
TextView txt3 = (TextView) rl.getChildAt(3);
txt3.setText(enterprises.getEnterprises().get(i).getCountry());
LinearLayout relativeLayout = (LinearLayout) mInflater.inflate(R.layout.rtl_enterprise, parent);
relativeLayout.setId(i);
everything there works kinda ok, but i have a problem, i need to create a
onClick listener
Why?
I would call a method which needs some info about the card that has been clicked, and then redirect to a new activity which contains the info about the card that he clicked.
But i would need that onclick listener for each of these relative layouts, and i assume it would need to be initialized when the card is created since it create + 50 cards, but i have no idea of how to setup each listener.
Why do you create it like that?
a better solution to use RecyclerView.
and in the adapter, you can listen for the item click. write the code one time and when any item clicked. you will know the position of the clicked item.
see this tutorial
https://developer.android.com/guide/topics/ui/layout/recyclerview
https://www.androidhive.info/2016/01/android-working-with-recycler-view/

How can I create a TextView programmatically in a Linear Layout?

I'm trying to make a TextView programmatically in a LinearLayout. The program includes a checking system to check if its been added already and the prompt for creating the textview is an option in a spinner. Here is the full onClick method for the spinner
public void onClick(String Ingredient, int i) {
Toast.makeText(Kitchen.super.getContext(), "Selected "+Ingredient, Toast.LENGTH_SHORT).show();
if(Ingredient.equals(tomatoSauce.name)) {
if (tomatoSauce.init == 0){
tomatoSauce.init = 1;
TextView one = new TextView(getContext());
one.setText(Ingredient);
mainll.addView(one);
}
} else if(Ingredient.equals(chicken.name)) {
chicken.init = 1;
} else if(Ingredient.equals(olives.name)){
olives.init = 1;
}
}
The Linear layout is identified from the xml layout when the app is started in a separate method.
final LinearLayout mainll = (LinearLayout) getActivity().findViewById(R.id.main);
The app crashes upon selecting Tomato Sauce from the menu despite the lack of identified coding errors. Any help with this issue would be greatly appreciated.
Try to add below lines of code:
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linear_layout_id);
TextView tv = new TextView(this);
tv.setText("hallo hallo");
tv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
linearLayout.addView(tv);

How to get data from dynamically created EditText and TextView inside a linearlayout?

In my main.xml I have a LinearLayout:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/linearMain"
>
</LinearLayout>
Then, inside my Main.java I am retrieving values from the DB (name and price of an item). Then, I dynamically created my form using the below code:
Cursor cursor = mydb.getAllJuice();
lm = (LinearLayout) findViewById(R.id.linearMain);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT);
while (cursor.moveToNext()) {
int cid = cursor.getInt(0);
String id = Integer.toString(cid);
String name = cursor.getString(1);
String price = cursor.getString(2);
// Create LinearLayout
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.HORIZONTAL);
// Create TextView
TextView product = new TextView(this);
product.setText(name+" ");
ll.addView(product);
// Create TextView
TextView pricetxt = new TextView(this);
pricetxt.setText(" "+price);
ll.addView(pricetxt);
// Create TextView
TextView currency = new TextView(this);
currency.setText(" LL ");
ll.addView(currency);
// Create TextView
TextView qtylabel = new TextView(this);
qtylabel.setText("QTY ");
ll.addView(qtylabel);
EditText qty = new EditText(this);
qty.setMinLines(1);
qty.setMaxLines(3);
ll.addView(qty);
lm.addView(ll);
}
// Create LinearLayout
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.HORIZONTAL);
final Button btn = new Button(this);
// Give button an ID
int j = 122;
btn.setId(j+1);
btn.setText("Add To Cart");
// set the layoutParams on the button
btn.setLayoutParams(params);
//Add button to LinearLayout
ll.addView(btn);
//Add button to LinearLayout defined in XML
lm.addView(ll);
The user will be able to enter the number of items and consequently onClick of the button a TextView will be manipulated.
To fill this textview I will need to loop on all the items that were created to check the price and check how the number that the user entered. I tried using the below code, however I am not able to access the specific item that I want as I have in each row 2 TextView and 1 editText:
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// code will be here
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
total = 0 ;
View v1 = null;
for(int i=0; i<lm.getChildCount(); i++) {
v1 = lm.getChildAt(i);
}
}
});
How can I access the different element in the v1?
You can use tag parameter of your Views (TextView, EditText etc.): setTag(Object) . Tags are essentially an extra piece of information that can be associated with a view. They are most often used as a convenience to store data related to views in the views themselves rather than by putting them in a separate structure. Check Android documentation for more details.
P.S. Instead to use your approach, you can use RecyclerView (or older ListView) with ViewHolder. This already mentioned in comments.
Answer to your question:
The problem is that your "product", "pricetxt", "btn" variables are local variables and gets garbage collected after your current loop iteration is over.
Well you can implement view holder pattern in your project.
Create a class named ViewHolder
class ViewHolder{
LinearLayout ll;
TextView product;
TextView pricetxt ;
TextView currency ;
TextView qtylabel ;
EditText qty ;}
public ViewHolder(LinearLayout ll TextView product TextView pricetxt TextView currency TextView qtylabel EditText qty)
{
this.l1= l1;
this.product=product;
this.pricetxt=pricetxt;
this.currency = currency;
this.qtylabel = qtylevel;
this.qty = qty;
}
This class can hold all your data by passing all these parameters (qty, pricetxt, etc).
Now you have to maintain a List for this ViewHolder Object at the top. You can do this as-
List myList<ViewHolder> = new ArrayList<ViewHolder>();
At the end of every iteration, create and add the ViewHolder object;
You can do this as follows
ViewHolder currentView = new ViewHolder(l1.product,pricetxt,currency,qtylabel,qty);
myList.add(currentView);
myList.add(currentView);
You can access any ViewHolder object maintained inside the list at position "index" as
myList.get(index);
Or the EditText "qty" as
myList.get(index).qty;
In this way you can access your created EditTexts and TextView even after the loop iteration is over.
My Suggestion:- No one does it in this way. As suggested some guys over here you should you android implement recommended RecyclerView which is much more efficient than the current implementation. In cases you can even you ListView.

How can you add multiple button listeners all at once?

Scroll down for my answer. The question doesn't really matter and the code is just confusing.
Is there a function that will allow me to fetch the id labels so I can loop a button listener easily? Currently I have in my main "container" xml that houses fragments this line of code:
public static final Map<String, Integer> RMAP = createMapR();
// Map of widget id's in R
private static Map<String, Integer> createMapR() {
Map<String, Integer> result = new HashMap<String, Integer>();
for (Field f : R.id.class.getDeclaredFields()) {
String key = f.getName();
Integer value = 0;
try {
value = f.getInt(f);
}
catch (IllegalArgumentException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
result.put(key, value);
}
return Collections.unmodifiableMap(result);
}
and then one of my fragments will pick up the RMAP, and cross that with the labels I have specified. I'm doing this because I have a few buttons and specifying a huge list of listeners seemed inefficient, then I got sidetracked on this code.
public class BottomFragment extends Fragment {
private final String[] LABELS = {"button_do_1", "button_woah_2", "button_foo_1",
"button_hi_2"};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_fragment, container, false);
for (String item : LABELS) {
if (Container.RMAP.containsKey(item)) {
((Button) v.findViewById(Container.RMAP.get(item)))
.setOnClickListener(this);
}
}
return v;
}
However if there was a way to iterate through the list of android:id items specifically to BottomFragment() I wouldn't have to even need the first block of code and it would eliminate the manual list of ID's I've typed in.
Here's a rough logic sketch (in C#)... It basically does a single, top down pass over the layout, building an id-to-view map that can be looped through afterwards. There is know general way to do this, and so will have to be maintained along with the layout file (both need to be modified together).
Say you want to do this in OnCreate of the Activity that is hosting your fragment(s) (it could possibly be done in the Fragments OnMeasure or OnLayout overrides too)
Assuming your fragment's layout file has the following structure
LinearLayout
TextView
TextView
RelativeLayout
EditText
EditText
TextView
public override OnCreate()
{
Map<int, view> idLabels = new Map<int, view>();
View v = fragment.View;
LinearLayout ll = (LinearLayout) v;
idLabels.Add(ll.Id, ll)
TextView tv1 = (TextView) ll.GetChildAt(0);
idLabels.Add(tv1.Id, tv1)
TextView tv2 = (TextView) ll.GetChildAt(1);
idLabels.Add(tv2.Id, tv2)
RelativeLayout rl = (RelativeLayout) ll.GetChildAt(2);
idLabels.Add(rl.Id, rl)
// notice the numbers being passed to GetChildAt, its a traversal of Views and Viewgroups !
EditText et1 = (EditText) rl.GetChildAt(0);
idLabels.Add(et1.Id, et1)
EditText et2 = (EditText) rl.GetChildAt(1);
idLabels.Add(et2.Id, et2)
// now, go back "up" to top-most, linear layout viewgroup and get remaining views, if any
TextView tv3 = (TextView) ll.GetChildAt(3);
idLabels.Add(tv3.Id, tv3)
...
foreach(int id in idLabels)
{
if(id == R.id.myViewIdLabel) // your R file will have all your id's in the literal form.
{
View v = idLabels.Map(id);
v.SetOnClickListener(this);
}
}
}
This may not be the best way, but it should work.
I figured it out. The solution lies within the children of a ViewGroup. This code adds listeners for every button in that particular view. In this case the view is a fragment of only buttons. This code is handy for whenever you have lots of buttons and you want to add a listener to each one.
public static void addButtonListeners(View v, OnClickListener clickListener)
{
ViewGroup widgets = (ViewGroup) v;
for(int i = 0; i < widgets.getChildCount(); ++i)
{
View child = widgets.getChildAt(i);
if (child instanceof Button)
{
Button nextButton = (Button) child;
nextButton.setOnClickListener(clickListener);
}
}
}
You need to pass the view and listener to this function. The listener can by passed by just passing "this", i.e:
addButtonListeners(v, this);

Categories