Want to use same Adapter with different Activities and different row_layouts - java

I want my Adapter to choose either one row_layout or another row_layout, depending on which Activity is sending the data. There are two Activities sending data.
At the moment I only know how to make the Adapter use one row_layout. I can't work out how to write the extra code to make it choose a different row_layout, depending on which Activity is sending the data. (There wont be any checkbox in the second row_layout).
Here is my Adapter:
public class ShopItemAdapter extends ArrayAdapter<ShopItem> {
public ShopItemAdapter(Context context, ArrayList<ShopItem> shopItem){
super(context, 0, shopItem);
}
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.row_layout, parent, false);
}
TextView tvItem = (TextView)convertView.findViewById(R.id.tvItemName);
TextView tvQty = (TextView)convertView.findViewById(R.id.tvQuantity);
CheckBox cbIsPurchased = (CheckBox)convertView.findViewById(R.id.cbIsPurchased);
ShopItem _shopItem = getItem(position);
tvItem.setText(_shopItem.getItemName());
tvQty.setText(String.valueOf(_shopItem.getQuantity()));
if(_shopItem.getIsPurchased() == 1){
cbIsPurchased.setChecked(true);
}
else{
cbIsPurchased.setChecked(false);
}
return convertView;
}
}

You can add some parameter to the ShopItemAdapter constructor to distinguish from which Activity is called, but if that implies to use many if conditionals in getView code then i think it's better to write several adapters, you will have a much clear code.

Related

How to add checkbox on all the listView items after having a long item click? Android Java

I want to add a feature on my project where if the user clicks one of the items on the listView then a checkbox would appear allowing the user to delete 1 or many items at once. Similar to Samsung Notes when you want to delete notes and or folders, etc. However, this concept is completely foreign to me and currently, I don't know where to begin to start this or what topic/resource/sample code I should look for. Also, I have a custom Array Adapter class that I used to order to work with my ListView but it came to my knowledge that you only need 1 array adapter class to make this work which made me confused since I don't know where to begin to manipulate it even further. Any help would be amazing!
Here is my Array Adapter that I have at the moment
//want to create our own custom ArrayAdapter. Going to extends the base class ArrayAdapter and hold our
//Word object
public class WordAdapter extends ArrayAdapter<WordFolder> {
//constructor - it takes the context and the list of words
WordAdapter(Context context, ArrayList<WordFolder> word){
super(context, 0, word);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View listItemView = convertView;
if(listItemView == null){
listItemView = LayoutInflater.from(getContext()).inflate(R.layout.folder_view, parent, false);
}
//Getting the current word
WordFolder currentWord = getItem(position);
//making the 3 text view to match our word_folder.xml
TextView date_created = (TextView) listItemView.findViewById(R.id.date_created);
TextView title = (TextView) listItemView.findViewById(R.id.title);
TextView desc = (TextView) listItemView.findViewById(R.id.desc);
//using the setText to get the text and set it in the textView
date_created.setText(currentWord.getDateCreated());
title.setText(currentWord.getTitle());
desc.setText(currentWord.getTitleDesc());
return listItemView;
}
}```
In R.layout.folder_view add one and make it invisible or gone. OnLongClick make them Visible.

2 different layouts for the type of each row

I know that there are similar questions, but i couldn't really understand, so i decided to ask again.
I am trying to do something similar to a chat, so in the list view there would be 2 types of rows, the ones of the received items and the ones of the sent items. The ones that were received will inflate the recvRow.xml layout, and the ones that were sent will inflate the sentRow.xml layout.
I have a list of items that are suposed to be shown ( name, img, etc) and i have a boolean that represents if the object was sent or received.
The problem is that those layouts have different buttons and if for example i receive something first, it will obviously load the recvRow.xml , but then if I try to send something, it will throw a nullpointerexception because on one of the buttons that only exists in the sentRow.xml layout (and doesn't exist in the recvRow.xml).
Since i am checking that boolean that i told you guys before, i don't see the need to use the getItemViewType method.
Here is my code for the getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
viewHolder = null;
if (convertView == null) {
viewHolder=new ViewHolder();
LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
System.out.println("Transf Rec Adapter : Going to draw AN ITEM : "+ listOfItems.get(position).getAppName());
System.out.println("Transf Rec Adapter : The received bool is : "+ listOfItems.get(position).isReceived());
if(listOfItems.get(position).isReceived()){
view=layoutInflater.inflate(R.layout.highway_transf_record_recv_row,parent,false);
viewHolder.deleteFile=(ImageView) view.findViewById(R.id.transfRecRowDelete);
viewHolder.installButton = (ImageView) view.findViewById(R.id.transfRecRowInstall);
}else{//se enviado
view=layoutInflater.inflate(R.layout.highway_transf_record_sent_row,parent,false);
viewHolder.reSendButton=(ImageView) view.findViewById(R.id.transfRecReSendButton);
}
viewHolder.appImageIcon=(ImageView) view.findViewById(R.id.transfRecRowImage);
viewHolder.appNameLabel=(TextView) view.findViewById(R.id.transfRecRowText);
viewHolder.appVersionLabel = (TextView) view.findViewById(R.id.transfRecRowAppVersion);
viewHolder.senderInfo = (TextView) view.findViewById(R.id.apkOrigin);
viewHolder.rowImageLayout = (RelativeLayout) view.findViewById(R.id.transfRecRowImageLayout);
viewHolder.appInfoLayout = (RelativeLayout) view.findViewById(R.id.appInfoLayout);
}else{
view= convertView;
viewHolder = (ViewHolder) convertView.getTag();
}
if(listOfItems.get(position).isReceived()){
System.out.println("INSIDE THE GET VIEW TRANSF REC ADAPTER : RECEIVED SOMETHING");
viewHolder.senderInfo.setText("You received this app :");
myOnClickListenerToInstall = new MyOnClickListenerToInstall(position);
viewHolder.installButton.setOnClickListener(myOnClickListenerToInstall);
myOnClickListenerToDelete= new MyOnClickListenerToDelete(position);
viewHolder.deleteFile.setOnClickListener(myOnClickListenerToDelete);
}else{
System.out.println("INSIDE THE GET VIEW TRANSF REC ADAPTER : SENT SOMETHING");
viewHolder.senderInfo.setText("You sent this app :");
ReSendListener reSendListener=new ReSendListener(position);
viewHolder.reSendButton.setOnClickListener(reSendListener);//null pointer exception here
}
viewHolder.appNameLabel.setText(listOfItems.get(position).getAppName());
viewHolder.appImageIcon.setImageDrawable(listOfItems.get(position).getIcon());
viewHolder.appVersionLabel.setText(listOfItems.get(position).getVersionName());
view.setTag(viewHolder);
return view;
}
The id's are right and everything exists on the viewHolder.
You need to use getItemViewType because of recycling. Otherwise you can try to recycle a received item into a non-received view, which will screw things up royally. However it is ok for getItemViewType to just return 0 or 1 based on that boolean. It doesn't need to be very complicated for 2 view types.
You should override getItemViewType() in your adapter. This method will indicate the type of view which should be inflated for each row. You also need to override getViewTypeCount().

Android bubble chat different layout after first user message

I am creating "bubble" style messages representation in my chat application's listview adapter. This is the effect that I want to achieve:
As you can see, second user message should show up in different layout (without picture) if user wrote two or more messages in a row. How I can achieve this? Because seems it's quite complicated. Should I check last message type in adapter (doesn't look so reliable solution)? Or add and later check additional field in my message object? Any example or help would be appreciated.
I assume you have a Message class like this:
class Message {
public String text;
public String avatar;
}
So, in getView method, you can easy check previous item:
ArrayList<Message> mMessages;
...
public View getView (int position, View convertView, ViewGroup parent){
if( convertView == null ){
if (position > 0 && mMessages.get(position-1).avatar.equal(mMessages.get(position).avatar)){
// set layout for second message style
convertView = inflater.inflate(R.layout.list_without_avatar, parent, false);
} else {
// set layout for the first message style
convertView = inflater.inflate(R.layout.list_with_avatar, parent, false);
}
}
...
return convertView;
}
Hope it help!

Multiple listeners

I've a question about ListView and listeners.
Suppose i have a listview in my app. Each item of it contains a checkbox.
And i've the following code:
public View getView(final int position, View convertView, ViewGroup parent) {
CheckBox checkbox = (CheckBox)v.findViewById(R.id.checkbox);
checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// some code
}
});
}
As you can see I set setOnCheckedChangeListener every time when getView method is called.
So is it right way to set listener? Or should I set it once? Is it bad in terms of performance? Or it doesn't matter? And if I set the listener multiple times whether it will overwrite previous listener or will i have multiple listeners for this event?
Each listener that you set will overwrite the previous one (if any). Setting a listener, or anything else, will consume a super small amount of time, however here you're also creating a new anonymous class which will take longer.
For the maximum performance, I would make a few adjustments:
Use convertViews to cache the views
Use ViewHolder pattern to avoid multiple findViewById calls
Use a single listener for all checkboxes and set it only on the cached views (if convertView == null)
Save the position of the item that the CheckBox is located in as the CheckBox's tag
Here's an example:
private static class ViewHolder {
CheckBox checkBox;
}
private CompoundButton.OnCheckedChangeListener mCheckListener = new CompoundButton
.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.e("TAG", "CheckBox position: " + buttonView.getTag());
}
};
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
/* Inflate the layout here */
holder = new ViewHolder();
holder.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
/* Find other views in you layout here */
holder.checkBox.setOnCheckedChangeListener(mCheckListener);
// Set the holder as tag, so you can fetch it later for re-use
convertView.setTag(holder);
} else {
// Fetch the holder
holder = (ViewHolder) convertView.getTag();
}
// Set the checkbox position
holder.checkBox.setTag(position);
return convertView;
}
If you're not familiar with using convertViews and the ViewHolder pattern you should watch The World of ListView that explains it all.
Is it bad in terms of performance? Or it doesn't matter?
It does not matter. The setter just assign the object.
And if I set the listener multiple times whether it will overwrite
previous listener or will i have multiple listeners for this event?
you will have just one for each instance of the checkbox upon you called setOnCheckedChangeListener. The ListView is particular case due of its recycling mechanism

GreenDroid PagedView - Multiple pages (different layout) help.. Android SDK

I've spent the day playing around with the GreenDroid PagedView and PagedAdaptor to create multiple screens that one can swipe through (a la iPhone style and Android Home Screen style). It's a very cool effect and the GreenDroid library is awesome. Soo.. I can get this working for one XML layout (code below), but what I am attempting to do is have three screens with different layouts. I've played around and got it working successfully with one method, but am unsure whether the method I'm using will cause problems due to repeat inflation of XML layout at every method call. I'll give some examples of what I've tried to save time. I'm hoping someone reads this that has had experience with GreenDroid and can point me in the right direction. Maybe this will also help someone else too..
Here is my code:
HomePageActivity.java
public class HomePageActivity extends GDActivity {
private static final int PAGE_COUNT = 3;
private final Handler mHandler = new Handler();
private PageIndicator mPageIndicator;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setActionBarContentView(R.layout.homepage_view); // container xml
final PagedView pagedView = (PagedView) findViewById(R.id.homepage_view);
pagedView.setOnPageChangeListener(mOnPagedViewChangedListener);
pagedView.setAdapter(new HomePageAdapter());
mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
mPageIndicator.setDotCount(PAGE_COUNT);
setActivePage(pagedView.getCurrentPage());
}
private void setActivePage(int page) {
mPageIndicator.setActiveDot(page);
}
private OnPagedViewChangeListener mOnPagedViewChangedListener = new OnPagedViewChangeListener() {
#Override
public void onStopTracking(PagedView pagedView) {
}
#Override
public void onStartTracking(PagedView pagedView) {
}
#Override
public void onPageChanged(PagedView pagedView, int previousPage, int newPage) {
setActivePage(newPage);
}
};
private class HomePageAdapter extends PagedAdapter {
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
Log.i("getView","ConvertView is null");
convertView = getLayoutInflater().inflate(R.layout.homepage_one, parent, false);
}
Log.i("getView Page",Integer.toString(position));
return convertView;
}
}
The two lines:
Log.i("getView","ConvertView is null");
Log.i("getView Page",Integer.toString(position));
I am using to debug this to see what is happening via. LogCat and so I can experiment.
The first line is displayed ONCE - first time application is created. I've noticed that VERY VERY occasionally as I swipe through the pages (I have three - set by variable PAGE_COUNT at top), that after 30-50 swipes I will see this message again. This shows that this layout is only INFLATED once - for all screens.
The second line I naturally see every time I change page. The integer position is either 0, 1 or 2 in this particular case.
The XML layout homepage_one is a simple layout with three TextViews. The XML layout homepage_view is the container for these pages.
homepage_view.xml
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:greendroid="http://schemas.android.com/apk/res/com.cyrilmottier.android.gdcatalog">
<greendroid.widget.PagedView
android:id="#+id/homepage_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/splash" />
<greendroid.widget.PageIndicator
android:id="#+id/page_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:paddingTop="10dp" />
Naturally I've tried some obvious methods such as this: (which works as wanted)
public View getView(int position, View convertView, ViewGroup parent) {
if (position == 0) {
convertView = getLayoutInflater().inflate(R.layout.homepage_one, parent, false);
} else if( position == 1 ){
convertView = getLayoutInflater().inflate(R.layout.homepage_two, parent, false);
} else {
convertView = getLayoutInflater().inflate(R.layout.homepage_three, parent, false);
}
return convertView;
}
This works as intended. Every screen is different as I want. I've not noticed any performance problems during testing, but it's worth noting in the original instance that we only inflate the layout ONCE. As this method is called (which is every time the page changes, we inflate the layout again. This doesn't seem resourceful, but then again this is my first week with Android development and I don't know how the architecture works... (just reading, reading, reading and testing, testing, testing) - so hence why I would like to hear from more experienced and knowledgeable developers. This may well be acceptable.. I would like to hear from you.
I tried this:
public View getView(int position, View convertView, ViewGroup parent) {
if (PAGE_ONE == false && position == 0) {
convertView = getLayoutInflater().inflate(R.layout.homepage_one, parent, false);
PAGE_ONE = true;
} else if(PAGE_TWO == false && position == 1 ){
convertView = getLayoutInflater().inflate(R.layout.homepage_two, parent, false);
PAGE_TWO = true;
} else if( PAGE_THREE == false && position == 2 ){
convertView = getLayoutInflater().inflate(R.layout.homepage_three,parent, false);
PAGE_THREE = true;
}
return convertView;
}
PAGE_ONE, PAGE_TWO, PAGE_THREE were set in the class as private boolean = false. The consideration behind this is that we only inflate layout once and every other call to this method simply returns the already inflated xml layout. Unfortunately this crashes my emulator and my phone (HTC EVO 3D). As Jay-Z would say, on to the next one...
Any ideas guys?
The GreenDroid's PagedView is normally intended to display a large set of pages. It can be considered as an horizontal paged ListView. This is the reason why using it is very close to the common ListAdapter.
In your case, you want to keep three pages in memory. This is completely possible but not recommended as you are preventing the PagedView from recycling your pages and also increasing the amount of View in memory.
Having only 3 pages (with a relatively flat and narrow View hierarchy) is OK. A better way do to what you are trying to do would be to lazily-initialize the pages and keep a reference on each pages.
private static final int PAGE_COUNT = 3;
private View[] mPages = new View[PAGE_COUNT];
public View getView(int position, View convertView, ViewGroup parent) {
if (mPages[position] == null) {
View page = createPageAtPosition(position, parent);
mPages[position] = page;
}
return mPages[position];
}
Of course you may have a warning in your logs as the PagedView notify the developer when the convertView is not reused.
I ran into a similar issue as well. Not sure if it is a bug with GreenDroid or user error on my part. (The latter is more likely)
Anyway, here is the solution I came up with. Your solution was nearly there, but you missed the critical 'else' condition when grabbing an existing view. It's working quite well for me.
/** get the view to display */
#Override
public View getView(int position, View convertView, ViewGroup parent) {
pageEnum currentPage = pageEnum.values()[position];
if(null == mViews[position]) {
switch(currentPage) {
case PAGE_1:
convertView = getLayoutInflater().inflate(R.layout.page1, parent, false);
break;
case PAGE_2L:
convertView = getLayoutInflater().inflate(R.layout.page2, parent, false);
break;
case PAGE_3:
convertView = getLayoutInflater().inflate(R.layout.page3, parent, false);
break;
default:
convertView = null;
Log.e(TAG, "Invalid page.");
break;
} /* switch(currentPage) */
mViews[position] = convertView;
} /* if(null == mViews[position]) */
else {
convertView = mViews[position];
}
return convertView;
} /* getView() */

Categories