having one spinner in each card view - card view in recyclerview - android

I am following this tutorial https://www.simplifiedcoding.net/android-recyclerview-cardview-tutorial/ However my requirements are different.
Instead of having set values like a name, description, rating like the tutorial has, I am wanting to have a spinner, edit text, textbox and checkbox in each Cardview. (when the user presses the FAB button, another card view is added, which also has a spinner, textbox, checkbox and edit text)
But the thing is, the spinner is populated by items in a csv file and this is where I am having trouble. I have a previous program file without card view and recycler view, where the spinners are populated with csv file values and when you press the FAB, spinner appears. However im having trouble integrating the recycler view with this.
In my previous program file I had a MyListAdapter.java.This is the adapter for the spinner i believe.
public class MyListAdapter extends ArrayAdapter<String> {
int groupid;
List<String> items;
Context context;
String path;
public MyListAdapter(Context context, int vg, int id, List<String> items) {
super(context, vg, id, (List<String>) items);
this.context = context;
groupid = vg;
this.items = items;
}
static class ViewHolder {
public TextView textid;
public TextView textname;
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
{
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(groupid, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.textid = (TextView) rowView.findViewById(R.id.txtid);
viewHolder.textname = (TextView) rowView.findViewById(R.id.txtname);
rowView.setTag(viewHolder);
}
// Fill data in the drop down.
ViewHolder holder = (ViewHolder) rowView.getTag();
String row = items.get(position);
//holder.textid.setText(row[0]); //prints aisle number, dont need
holder.textname.setText(row);
return rowView;
}
}
}
Additionally then, in my mainacttivity I had this code which read the csv file.
private class CSVFile {
InputStream inputStream;
public CSVFile(InputStream inputStream) {
this.inputStream = inputStream;
}
public List<String> read() {
List<String> resultList = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line;
while ((line = reader.readLine()) != null) {
String[] row = line.split(",");
//TODO I edited this part so that you'd add the values in our new hash map variable
numberItemValues.put(row[1], row[0]);
resultList.add(row[1]);
}
} catch (IOException e) {
Log.e("Main", e.getMessage());
} finally {
try {
inputStream.close();
} catch (IOException e) {
Log.e("Main", e.getMessage());
}
}
return resultList;
}
}
But the tutorial asks for a two classes, Product.java and product adapter.java. (if you click the tutorial you will see their code) I am confused how to integrate my code above with this tutorial situation?

*-first of all create your recyclerview row layout xml file with items you want
*-load csv file in your main activity or fragment and pass them to your adapter, declare a parameter in your adapter constructor
*-inside your recuclerview adapter class, in onCreateViewHolder method inflate that layouts(just like example)
*-in your RecyclerView.ViewHolder constructor get all your comonent like Spinner and etc, just like example
TextView textViewTitle = itemView.findViewById(R.id.textViewTitle);
...
Spinner spinner = itemView.findViewById(R.id.spinner);
*-in your onBindViewHolder method fill spinner with loaded csv file, same as before
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_spinner_item, spinnerArray);
spinner.setAdapter(adapter);//mContext should be passed from your activity to your adapter, so you should add it to your adapter constructor

First, you don't need a custom adapter for your spinner if all you need is to display a simple spinner with list of strings. Add the following snippet to your onBindViewHolder() method to populate your spinner.
String[] dataArray = new String[]{"Spinner data1", "data 2"}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_spinner_item, dataArray);
holder.spinner.setAdapter(adapter);
Read the tutorial and follow every steps including creating a Product.java class. Create a layout and replace the layout_product.xml with the new layout. I have created one for you below, copy the code and rearrange the components to meet your want and paste it in the new layout file. Lets call the new file, card_layout.
card_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Spinner
android:padding="7dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:textAppearance="?android:textAppearanceMedium"
android:text="Hey, its me"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
I don't understand the TextBox part, but you can add it yourself if its not already the above code snippet. Now, replace the layout in recyclerview adapter with the new layout. It should work now.

Related

Android App Custom ListView: Add new item in the most efficient way

I am using a custom ListView, in which I need to add a new item. Since there will be a lot of items needed to be added, I want to code it in the most efficient way so the smartphone is not loosing a lot of memory. Here is the code of my custom ListView:
class MessageActivityAdapter extends ArrayAdapter<String>
{
Context context;
String[] message;
String time;
MessageActivityAdapter(Context c, String[] message, String time)
{
super(c, R.layout.messagescreen_list_row, R.id.textView1, message);
this.context = c;
this.message = message;
this.time = time;
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.messagescreen_list_row, parent, false);
TextView tvMessage = (TextView) row.findViewById(R.id.textView1);
tvMessage.setText(message[position][0]);
return row;
}
}
This is how I load the ListView on the first time:
adapter = new MessageActivityAdapter(this, values, time);
listView.setAdapter(adapter);
This is how I change the text in one of the items and update the ListView:
adapter.message[itemPos] = newItemText;
adapter.notifyDataSetChanged();
And thats how I thought I can add a new item to the ListView. Basically I copy the old array "message" and create a new array "messageNew" which has a +1 bigger size. Then I use "messageNew" as the new array in my MessageActivityAdapter and update the adapter. The problem: copying and creating a new array costs a lot of memory. Why I don't use an ArrayList? Because later I will change the array into a multidimensional array and multidimensional ArrayLists don't exist.
String[] messageNew = getNewMessageArray();
adapter.message = messageNew;
adapter.notifyDataSetChanged();
I'm also not sure if changing the "message" array and using the notifyDataSetChanged() method is the best way at all...

add button to only one row in android listView

I am trying to add a button in the middle of my listView. Ideally The button will split the listView and it will continue afterward, but if this is not possible I will be ok with a button inside a row in the listView.
For example. My list view will have line one (image + text) , line two ( image + text) , button, and go on with the list view.
I have wrote the following code. This adds a button to a row in listView, but on the way it also adds an empty button (a button with now text) to every row in my listView. In addition the gravity setting for center is not working.
My xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<ImageView android:id="#+id/imgUserIcon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:scaleType="fitStart" />
<Button
android:id="#+id/buttonShowHide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/showHide" />
<TextView android:id="#+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
/>
</LinearLayout>
my adapter
public class UserAdapter extends ArrayAdapter<UserAccountData> {
Context context;
int layoutResourceId;
User data[] = null;
public UserAdapter(Context context, int layoutResourceId,
UserAccountData[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
UserAccountDataHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new UserAccountDataHolder();
holder.imgIcon = (ImageView) row.findViewById(R.id.imgUserIcon);
holder.txtTitle = (TextView) row.findViewById(R.id.txtTitle);
holder.showHide = (Button) row.findViewById(R.id.buttonShowHide);
row.setTag(holder);
} else {
holder = (UserHolder) row.getTag();
}
User user = data[position];
holder.txtTitle.setText(user.title);
holder.imgIcon.setImageResource(user.icon);
holder.showHide.setText(user.buttonName);
return row;
}
static class UserHolder {
ImageView imgIcon;
TextView txtTitle;
Button showHide;
}
}
My Java object for the row. I have created two constructors one for the button and one for the image and text.
public class UserAccountData {
public int icon;
public String type;
public String title;
public CharSequence buttonName;
public UserAccountData(){
super();
}
// for image and text
public UserAccountData(int icon, String title, String type) {
super();
this.icon = icon;
this.title = title;
this.type = type;
}
// for button
public UserData(CharSequence buttonName, String type) {
super();
this.buttonName = buttonName;
this.type = type;
}
public void setType(String type){
this.type = type;
}
}
In my activity I am adding the following two rows to the array , that later my adapter will use to create the listView ( I am passing it an ArrayList that being changed into an Array)
user_data.add(new UserAccountData(icon, "title,"type"));
user_data.add(new UserAccountData("show Password","button"));
a) is there a way to split the listView and the middle and just add a button? and continue the same listView? Because my current solution tries to add a button to a row.
b) any ideas why I am actually also adding an empty button to the icon, title type row?
I am getting icon, title, empty button on my actual listView
Thank you very much
UPDATE:
Found two blogs
http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
and
http://android.amberfog.com/?p=296
, but still don't have any luck. Would appreciate some more in depth help
is there a way to split the listView and the middle and just add a button? and continue the same listView? Because my current solution tries to add a button to a row.
If I understand your question you want something like:
My list view will have:
image + text
image + text
button
image + text
etc...
You can have more than one type of row layout if you override getViewTypeCount() and getItemViewType().
getViewTypeCount() should return the number of types, in this case 2.
getItemViewType(int position) will return which type the row at position is, in this case either 0 or 1.
Addition
I don't really know how to make the distinction between the image text row and the button. I tried to find a way to see if my image is null (using the 2nd constructor) , but this does not seems to work
This sounds like a good approach, but since icon is an int it will never be null, the default value for an uninitialized integer is 0. Try:
#Override
public int getItemViewType(int position) {
UserAccountData data = getItem(position);
if(data.icon == 0)
return 1;
return 0;
// The same thing in one line:
//return getItem(position).icon == 0 ? 1 : 0;
}

Making one row in a ListView to a password (show and hide) row

I have created a customized listView using the following tutorial http://www.ezzylearning.com/tutorial.aspx?tid=1763429.
My list view includes two row (each with an image and a TextView). The first row is user, and the second is password.
I am looking for a way to make the password row to masked, something like ****, and to add another row that will enable the user to set it to visible/ mask.
I found the following examples,
How to show hidden password in textview?
How to switch between hide and view password
but I have no idea how to implement this on a specific row.
my rows xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<ImageView android:id="#+id/imgUserAccountIcon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:scaleType="fitStart" />
<TextView android:id="#+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
/>
</LinearLayout>
My user class
public class UserAccountData {
public int icon;
public String title;
public UserAccountData(){
super();
}
public UserAccountData(int icon, String title) {
super();
this.icon = icon;
this.title = title;
}
}
My adapter class
public class UserAccountAdapter extends ArrayAdapter<UserAccountData> {
Context context;
int layoutResourceId;
UserAccountData data[] = null;
public UserAccountAdapter(Context context, int layoutResourceId,
UserAccountData[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
UserAccountDataHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new UserAccountDataHolder();
holder.imgIcon = (ImageView) row.findViewById(R.id.imgUserAccountIcon);
holder.txtTitle = (TextView) row.findViewById(R.id.txtTitle);
row.setTag(holder);
} else {
holder = (UserAccountDataHolder) row.getTag();
}
UserAccountData userAccountData = data[position];
holder.txtTitle.setText(userAccountData.title);
holder.imgIcon.setImageResource(userAccountData.icon);
return row;
}
static class UserAccountDataHolder {
ImageView imgIcon;
TextView txtTitle;
}
}
and the appropriate list view snippet of the activity method
List<UserAccountData> user_data = new ArrayList<UserAccountData>();
user_data.add(new UserAccountData(R.drawable.username_icon,"userName");
user_data.add(new UserAccountData(R.drawable.password_icon,"password");
usersArray = new UserAccountData[user_data.size()];
user_data.toArray(usersArray);
UserAccountAdapter adapter = new UserAccountAdapter(this, R.layout.user_accounts_row, usersArray);
userAccountsListView = (ListView) findViewById(R.id.userAccounts);
userAccountsListView.setAdapter(adapter);
Attaching a picture of what I would like to accomplish
Before click:
after click
thanks
You could fill up a list of TextViews in your adapter while you inflated. After inflation, you'd have access to all the TextViews, and then in your onItemClickListener, when someone clicked the item in in Position 2 (in this case at least), you simply reference your list of TextViews, get the appropriate one, and change the parameters on the fly.
If you have a set, small number of rows like this a TableLayout is a better fit than a ListView. Then you could easily customize the xml of the two TextViews. Another option is to call setRawInputType to set the password on just the second TextView. You'd need to do that in the adapter's getView function based on the view's position.
Well I got around this problem by displaying the password as plain-text in an AlertDialog when the user clicks the ListView entry.

Listview item is not aligned properly

I have created on chat screen and i want to display sent message on left side and received messages right side.To do this i have created two xml one for right and one for left and i have bind this two xml with adapter.The problem is that when i am sending a text it resides on left side and when i am receiving a text,the sent text is also aligned on right side.and when i am sending again then whole text(chat) aligns on left.
Here is my ArrayAdapter:
private void setListAdapterL() {
ArrayAdapter<String> adapterL = new ArrayAdapter<String>(this,
R.layout.row_left, messages);
mList.setAdapter(adapterL);
}
private void setListAdapterR() {
// TODO Auto-generated method stub
ArrayAdapter<String> adapterR = new ArrayAdapter<String>(this,
R.layout.row_right, messages);
mList.setAdapter(adapterR);
}
My xml file for row_right.xml.in other row_left.xml just gravity is changed:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingLeft="5dip" />
Any ideas and suggestion will be appreciated.
Thanks
To do this you have to create custom adapter for list view instead of creating two separate adapters for left item (i.e., sent item) and right item (i.e., received item).
It means you have manage sent/received messages in one custom adapter.
for e.g.
// Sample messages inside MessageActivity
void initMessages() {
HashMap<String, String> messageSent = new HashMap<String, String>();
messageSent.put("message", "Hi");
messageSent.put("type", "sent");
messageSent.put("date", "");
HashMap<String, String> messageReceived = new HashMap<String, String>();
messageSent.put("message", "Hello");
messageSent.put("type", "received");
messageSent.put("date", "");
ArrayList<HashMap<String, String>> messages = new ArrayList<HashMap<String, String>>();
messages.add(messageSent);
messages.add(messageReceived);
MessageAdapter adapter = new MessageAdapter(mContext, messages);
setListAdapter(apater);
}
// adapter class
class MessageAdapter extends ArrayAdapter<HashMap<String, String>> {
LayoutInflater inflater;
public MessageAdapter(Context context, ArrayList<HashMap<String, String>> data) {
super(context, R.layout.raw_message, data);
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
convertView = inflater.inflate(R.layout.raw_message, null);
holder = new ViewHolder();
holder.txtSent = convertView.findViewByTag("sent");
holder.txtReceived = convertView.findViewByTag("received");
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
HashMap<String,String> message = getItem(position);
boolean sent = message.get("type").equals("sent");
if(sent) {
holder.txtSent.setVisibility(View.VISIBLE);
holder.txtSent.setText(message.get("message"));
holder.txtReceived.setVisibility(View.GONE);
} else {
holder.txtSent.setVisibility(View.GONE);
holder.txtReceived.setText(message.get("message"));
holder.txtReceived.setVisibility(View.VISIBLE);
}
}
class ViewHolder {
TextView txtSent;
TextView txtReceived;
}
}
// raw_message.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:tag="sent"/>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:tag="received"/>
</RelativeLayout>
The problem is that when i am sending a text it resides on left side and when i am receiving a text,the sent text is also aligned on right side.and when i am sending again then whole text(chat) aligns on left.
That is probably happening because each time you get a new message you set a new adapter for the ListView with the new gravity(left or right). Because the new adapter holds all messages in the chat they all get aligned left or right.
My advice is to use a data object class that will hold the actual message + its status(it's a received or sent message):
class Message {
String actualMessage;
int status = 0; //(for example 0 for sent messages and 1 for received messages)
public String toString() {
return actualMessage;
}
}
Then implement your own adapter and in the getView method see which type of message do you have and set the orientation for that particular row to left or right. This will also allow you to just update the list of messages instead of creating each time a new ArrayAdapter object. Adapter example:
public class CustomAdapter extends ArrayAdapter<Message> {
public CustomAdapter(Context context, int resId, List<message> data) {
super(context, resId, data);
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = (TextView) super.getView(position, convertView, parent);
Message msg = getItem(position);
if (msg.status == 0) {
tv.setGravity(Gravity.LEFT);
} else {
tv.setGravity(Gravity.RIGHT);
}
}
}
you're facing the problem that ALL you're items always get changed to the new layout. So everytime you're setting the left layout everything moves to the left side vice versa.
What you need is a custom adapter which enables you to have 2 textviews - one for the right side and one for the left one.

Can I make one ListView item have a different Text Color?

I have the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:id="#+id/ListView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1.0"
android:textColor="#android:color/white"
android:dividerHeight="1px"
android:listSelector="#drawable/highlight_sel"
/>
</LinearLayout>
And the code:
private ListView lv1;
private String lv_arr[]={"Item 1","Item 2","Item 3","Item 4"};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.newsmenu);
lv1=(ListView)findViewById(R.id.ListView01);
// By using setAdpater method in listview we an add string array in list.
lv1.setAdapter(
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
lv_arr));
}
I want the text color of Item 2 (or 1 or 3 or 4) to appear dynamically as red (denoting a new item) or white (default). Is there a way to do this?
I already have a selector present, which is why I used ListView. I've search the Internet and this site, and I have not seen this question broached.
So is it possible?
Yes everything is possible. you need to write your own adapter implementation basically overriding the getView Method in the adapter. search google and stack you will find many tutorials on how to write an adapter.
Writing a special adapter to override getView in simple adapter is the way to change the text color alternating on the lines of your choice in a listview. I took the example which has been repeated many times on this website and added a way to change the text color. position mod length to select the color position can be replaced with any scheme you like. The text view "business" can be the first line of your layout like mine--or use the android.R.id.text1.
public class SpecialAdapter extends SimpleAdapter {
private int[] colors = new int[] { 0x30FF0000, 0x300000FF };
public SpecialAdapter(Context context, List<HashMap<String, String>> items, int resource, String[] from, int[] to) {
super(context, items, resource, from, to);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
int colorPos = position % colors.length;
//view.setBackgroundColor(colors[colorPos]); //old example
TextView tv1 = (TextView)view.findViewById(R.id.business); //new
tv1.setTextColor(colors[colorPos]); //new
return view;
}
}
Just use SpecialAdapter instead of SimpleAdapter in your app.
Here's an example of a getView method. Note that it's using a viewholder for efficiency. If you want to know more about that, let me know.
public View getView(int position, View convertView, ViewGroup parent) {
tempDeal = exampleBoxArrayList.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = inflator.inflate(R.layout.list_item_example_box, null);
holder = new ViewHolder();
holder.divider = (RelativeLayout) convertView.findViewById(R.id.example_box_divider);
holder.merchantName = (TextView) convertView.findViewById(R.id.example_box_merchant_name);
holder.expireDate = (TextView) convertView.findViewById(R.id.example_box_expire_date);
holder.description = (TextView) convertView.findViewById(R.id.example_box_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (tempDeal.isDivider()) {
holder.divider.setVisibility(View.VISIBLE);
} else {
holder.divider.setVisibility(View.GONE);
}
holder.merchantName.setText(tempDeal.getMerchantName());
holder.expireDate.setText(tempDeal.getExpiryDateString());
holder.description.setText(tempDeal.getPriceOption().getDescription());
return convertView;
}
As you can see, I call the isDivider() method on my custom object (this method looks at a boolean set on data load). This method is used to turn the visibility of part of the layout on or off.
Alternatively, you could load a completely new layout based on this same concept.

Categories

Resources