Position views dynamically using addView() - android

I have 3 different viewgroups that need to be added in a LinearLayout. I'm using addView() to add it.
However, the adding is based on the response that my web service is returning. If there is no data, it will make a callback to the UI that the view will be empty.
Essentially, there are 3 views which are Featured, Latest and Categories. I want Featured to be at the top, followed by Latest and Categories.
I'm calling the web service like so,
public void loadFromApis() {
dealsService.getFeaturedDeals(this);
dealsService.getLatestDeals(this);
dealsService.getDealsCategories(this);
}
Example of successful callback (with data) and view adding:
#Override
public void onFeaturedSuccess(List<FeaturedModel> model) {
View view1 = DealsPanel.build(this, model);
linearLayout.addView(view1, 0);
}
#Override
public void onLatestSuccess(List<LatestModel> model) {
View view2 = DealsPanel.build(this, model);
linearLayout.addView(view2, 1);
}
#Override
public void onCategoriesSuccess(List<CategoriesModel> model) {
View view3 = DealsPanel.build(this, model);
linearLayout.addView(view3, 2);
}
I've tried using the index parameter to set the position, but since I'm loading the view based on API response, the layout wouldn't know which view to be draw first, so initializing the index would result in IndexOutOfBoundsException error.
My question is, based on this requirements, how can I statically define the position of each view to be added first and so forth? Any suggestions on improving the structure of this code?
Thanks in advance

One approach would be to statically define 3 child layouts in code or in XML inside of your parent LinearLayout, and then add your new views to the child layouts. That will preserve their order. For example:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout android:id="#+id/featuredDealsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout android:id="#+id/latestDealsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout android:id="#+id/dealCategoriesLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Then, assuming you initialize variables containing the new layouts (ie featuredDealsLayout) you could change your code to something like:
#Override
public void onFeaturedSuccess(List<FeaturedModel> model) {
View view = DealsPanel.build(this, model);
featuredDealsLayout.addView(view);
}
#Override
public void onLatestSuccess(List<LatestModel> model) {
View view = DealsPanel.build(this, model);
latestDealsLayout.addView(view);
}
#Override
public void onCategoriesSuccess(List<CategoriesModel> model) {
View view = DealsPanel.build(this, model);
dealCategoriesLayout.addView(view);
}

Related

View Pager layouts not keep match parent after switch views

I have four layouts inside of my viewpager that are supposed to match width/height of its parent (the viewpager). When the activity starts the first page appears to do that, but as soon as you swipe to the next one this weird gap appears. The gap will stay until the activity is restarted. How do I get rid of this gap?
Here's a video of what's happening: https://youtu.be/EA51HZuFruY
XML of one of the layouts (the other three are identical only having different background colors)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:background="#color/blue">
<Button
android:id="#+id/tab1_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:onClick="tab1Click"/>
</RelativeLayout>
Code for the viewpager's adapter
private class TabPagerAdapter extends PagerAdapter {
private ArrayList<Integer> layouts = new ArrayList<>();
TabPagerAdapter() {
layouts.add(R.layout.tab_menu_1);
layouts.add(R.layout.tab_menu_2);
layouts.add(R.layout.tab_menu_3);
layouts.add(R.layout.tab_menu_4);
}
#Override
public int getCount() {
return layouts.size();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == (View) object;
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(layouts.get(position), collection, false);
collection.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup collection, int position, Object view) {
collection.removeView((View) view);
}
}
EDIT: My layouts inside the viewpager are not matching parent if more than two views are in the viewpager. I can also tell it's not the viewpager not keeping it's constraints that I've given them.
EDIT: After about a month I figured out that changing the offscreen page limit to support the maximum number of screen that could ever be offscreen stops the problem. While I'd be fine with this it's causing slow down in the app, but maybe this might reveal the actually problem.

RecyclerView inside NestedScrollView causes RecyclerView to inflate all elements

I'm having an issue with placing a RecyclerView inside a NestedScrollView, which causes ALL elements of the RecyclerView's adapter to be rendered.
This is a rather large issue, as the lists that the RecyclerView is showing can contain several hundred elements.
This is at the moment causing quite a lot of lag (obviously) as it has to render all views at once, and can't reuse any already inflated views as the RecyclerView normally does.
This is my current XML (Removed some bloat to minimize it):
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="90dp">
<!-- Some content -->
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Some more content -->
</LinearLayout>
<!-- Product list -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12dp"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"/>
</LinearLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
This is my onCreateView() from the Fragment that is inflating the view containing the NestedScrollView and RecyclerView:
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.category_content_fragment, container, false);
ButterKnife.bind(this, root);
List<Product> products = new ArrayList<>(); //This is populated by other means, not relevant to the issue
productsRecyclerView.setNestedScrollingEnabled(false);
productsRecyclerView.setHasFixedSize(true);
productsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
ProductsContentAdapter productsContentAdapter = new ProductsContentAdapter(products);
productsRecyclerView.setAdapter(productsContentAdapter);
return root;
}
I have seen this post about the issue:
How to put RecyclerView inside NestedScrollView?
But it doesn't mention a final fix to the issue sadly.
To clarify:
The RecyclerView scrolls perfectly, it shows at the correct time, but the issue is that it renders ALL of its children instantly, meaning possible several hundreds of elements, even though the screen only shows 5-6 at a time at max.
Please feel free to ask questions if more information is needed.
------- EDIT -------
After many failed attempts of other solutions, i ended up using Jeeva Nandhan's solution.
Prior to asking this question i knew that was a possible solution, but i had 11 different possible views that needed to fit into the RecyclerView, so i would've liked to avoid it.
After using different ViewTypes, it worked perfectly. I was afraid it would be very inefficient due to the high amount of ViewTypes, but it's buttery smooth.
I too have come across this issue... This is because both scrollview and RecyclerView are different in loading data, since the ScrollView acts as the parent in this case and we are using the below line in our code.
setNestedScrollingEnabled(false);
This will make the scroll slow and hang issue based on the Recyclerview data.
One way which I have used to solve this issue is adding header to the Recyclerview..
Here I'll explain it clearly.
lets assume this recyclerview is in our activity.
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
The adapter class will be like this, where we will add the header
public class SampleAdapter extends RecyclerView.Adapter {
private final int BODY = 1;
private final int HEADER = 2;
private List<String> data = null;
SampleAdapter(List<String> data) {
this.data = data;
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return HEADER;
}
return BODY;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case HEADER:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.inflate_header_layout, parent, false);
return new HeaderViewHolder(view);
default:
//Setting the Body view...
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.inflate_details, parent, false);
return new BodyViewHolder(view);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof BodyViewHolder) {
//Set the body content....
if (data != null && data.size() > 0) {
/** Since we have added one cell for header,
* we need to decrement the position and render the body view.
*
*/
int bodyPosition = position - 1;
}
} else if (holder instanceof HeaderViewHolder) {
//Set the header content...
}
}
#Override
public int getItemCount() {
//Sice we are going to add header, we are supposed increase the count by one...
return data.size() + 1;
}
}
by this there is no need for NestedScrollView and all the view will work in RecyclerView behavior...
Hope this is helpful :)
If you have large amount of data to display,show only some numbers of data first time than on scroll use loadMoreListener to get next data.

Pass data from RecyclerView Adapter to Parent Fragment

I have a screen with a EditText to find content and a RecyclerView.
The EditText is to find in my web server
The RecyclerView shows most popular tags used
So, What I want to do is, when the user click any of the elements of the RecyclerView List
that value selected is passed to editText.
I have a fragment which inflate the file which show all the stuff:
tags_list.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org............BuscarFragment">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/inputSearch"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableLeft="#android:drawable/ic_menu_search"
android:hint="#string/busqueda"
android:inputType="textVisiblePassword"
android:lines="1"
android:singleLine="true" />
<TextView
android:id="#+id/buscar_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/inputSearch" />
<TextView
android:id="#+id/etiquetas_populares"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/buscar_msg"
android:layout_marginLeft="16dp"
android:text="#string/etiquetas_populares"/>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/tags_list"
android:name="org............TagsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/etiquetas_populares"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context="org...........activities.TagsActivity"
tools:listitem="#layout/tags_list_content" />
</RelativeLayout>
</FrameLayout>
In TagsFragment.java I have this method:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.tags_list, container, false);
//RecyclerView Section
mRecyclerView = (RecyclerView) view.findViewById(R.id.tags_list);
mBuscarTagsAdapter = new BuscarTagsAdapter(getActivity(), tagMoreUsedRecyclerViewList);
mRecyclerView.setAdapter(mBuscarTagsAdapter);
addKeyListener();
return view;
}
Finally in my Adapter.
BuscarTagsAdapter.java
#Override
public void onBindViewHolder(final BuscarTagsViewHolder holder, int position) {
final int posicion = position;
//onClick for the list
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//When the user click I want to pass this value to my EditText, but
//I don't see how to do it from this Adapter
Toast.makeText(context, tags.get(posicion).getTagName(), Toast.LENGTH_SHORT).show();
}
});
}
Any idea?
Thanks
UPDATED and SOLVED:
Hi, #Brian Nam, thanks for help.
Made some changes.
First in Constructor as you suggested:
public BuscarTagsAdapter(Context context, List<TagMoreUsedRecyclerView> items, TagsListInterface tagsListInterface) {
this.context = context;
this.tags = items;
this.tagsListInterface = tagsListInterface;
}
but tagsListInterface is not initialized properly in TagsFragment,
so I added this in onCreate method:
tagsListInterface = new TagsListInterface() {
#Override
public void onTagClicked(String tagName, int posicion) {
EditText editText = (EditText) view.findViewById(R.id.inputSearch);
editText.setText(tagName);
}
};
Finally in onClick for the RecyclerView:
BuscarTagsAdapter.java
#Override
public void onBindViewHolder(final BuscarTagsViewHolder holder, int position) {
final int posicion = position;
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, tags.get(posicion).getTagName(), Toast.LENGTH_SHORT).show();
tagsListInterface.onTagClicked(tags.get(posicion).getTagName(), posicion);
}
});
}
Unfortunately, there's no "good" way to do this. I'd say instead of interfaces you should check out one of the event bus implementations like EventBus from Greenrobot.
https://github.com/greenrobot/EventBus
The good thing about using something like EventBus (basically the publisher-subscriber pattern) is that you spare a lot of anonym classes, interfaces but you also make the code less coupled. Not to mention any class can subscribe to the events so you can basically say "The user did this" and multiple fragments can react to it. This is especially useful if you do complex UI/animation stuff.
The bad thing about EventBus is that you start having a lot of events code quality suffers, and its sometimes hard to know what happens and where.
But as I said there's no "good" way to do this, thats just how the android framework works.
As #Brian Nam wrote, you can pass a reference to a TagsFragment instance to your BuscarTagsAdapter.
A similar but IMO a little bit more 'clean' solution would be to create an interface e.g.:
public interface TagsListInterface {
void onTagClicked(String tagName);
}
Then your TagsFragment would implement the interface and pass an instance of it to BuscarTagsAdapter. Then you would call the onTagClicked method of that interface when an element of the Recycler View is clicked.
Another solution is to communicate the RecyclerView with your Fragment via your Activity which, as I believe, is the most 'Android-like' one. It could also involve creating a custom interface but it is the Activity that would implement it instead of a Fragment. You can read more about it here. The article explains how to perform a communication between fragments but I believe it can show you the general idea so you can apply it to your situation.
Update:
In onCreateView method you are calling:
mBuscarTagsAdapter = new BuscarTagsAdapter(getActivity(), tagMoreUsedRecyclerViewList);
The only thing you have to change here is to update the constructor of BuscarTagsAdapter to receive an instance of TagsListInterface and then pass your implementation from TagsFragment. Then you are able to use it to call onTagClicked when an element is clicked.

React Native ViewPagerAndroid with multiple visible pages

I've been trying to use ViewPagerAndroid to show a carousel with "stops" at each item like so:
It appears react-native-carousel and react-native-swiper don't support these features yet on Android. I have a native ViewPager that does what I need it to do, but I'm having problems incorporating it into the React Native environment.
The native component CarouselContainer uses layout.xml and expects a ViewPager subcomponent.
<com.mycompany.ui.CarouselContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="190dp"
android:layout_height="280dp"
android:layout_gravity="center_horizontal" />
</com.mycompany.ui.CarouselContainer >
CarouselContainer is a FrameLayout subclass that gets a ref to the ViewPager in its onFinishInflate method. It calls setPageMargin on the ViewPager and setClipChildren(false) to render multiple ViewPager pages on the screen at the same time.
I took a look at the ViewPagerAndroid implementation, and it looks like it's basically a ViewPager set up by ReactViewPagerManager. The ReactViewPagerManager exposes the ViewPager's child views via the ViewGroupManager interface.
So I copied ReactViewPagerManager in my own CarouselViewPagerManager and ReactViewPager into CarouselViewPager.
The one thing new here is CarouselViewPagerContainer. The manager creates the container and the container creates the view pager instead of the manager creating the view pager directly. Since there's no layout xml the container instantiates the pager directly instead of getting it from onFinishInflate.
For some reason, I'm getting nothing rendered on the screen when I use it. I think it's due to having the CarouselViewPagerContainer view in between but I'm not sure where to even begin. Do I need to mess with LayoutShadowNode? Do I need to add the CarouselViewPager itself to the list of child views returned by the manager?
The Native UI Components documentation only mentions SimpleViewManager. :(
// Copy of ReactViewPagerManager
public class CarouselViewPagerManager extends ViewGroupManager<CarouselViewPagerContainer> {
#Override
protected CarouselViewPagerContainer createViewInstance(ThemedReactContext reactContext) {
return new CarouselViewPagerContainer(reactContext);
}
// ...
#Override
public void addView(CarouselViewPagerContainer parent, View child, int index) {
parent.addViewToAdapter(child, index);
}
#Override
public int getChildCount(CarouselViewPagerContainer parent) {
// Should the CarouselViewPager be also counted?
return parent.getViewCountInAdapter();
}
#Override
public View getChildAt(CarouselViewPagerContainer parent, int index) {
return parent.getViewFromAdapter(index);
}
#Override
public void removeViewAt(CarouselViewPagerContainer parent, int index) {
parent.removeViewFromAdapter(index);
}
}
public class CarouselViewPagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener {
private final CarouselViewPager mPager; // Copy of ReactViewPager
public CarouselViewPagerContainer(ReactContext context) {
mPager = new CarouselViewPager(context);
}
// ...
// Pass through to the CarouselViewPager
public void setCurrentItemFromJs(int item, boolean animated) {
mPager.setCurrentItemFromJs(item, animated);
}
/*package*/ void addViewToAdapter(View child, int index) {
mPager.addViewToAdapter(child, index);
}
/*package*/ void removeViewFromAdapter(int index) {
mPager.removeViewFromAdapter(index);
}
/*package*/ int getViewCountInAdapter() {
return mPager.getViewCountInAdapter();
}
/*package*/ View getViewFromAdapter(int index) {
return mPager.getViewFromAdapter(index);
}
}
If you know of a simpler way to tackle this problem I'm all ears!
I mean the only thing I'm using the FrameLayout container for is to center the first Viewpager page and occupy space so multiple pages show. :/
It's been such a struggle :(

Android ListView with different layouts for each row

I am trying to determine the best way to have a single ListView that contains different layouts for each row. I know how to create a custom row + custom array adapter to support a custom row for the entire list view, but how can I implement many different row styles in the ListView?
Since you know how many types of layout you would have - it's possible to use those methods.
getViewTypeCount() - this methods returns information how many types of rows do you have in your list
getItemViewType(int position) - returns information which layout type you should use based on position
Then you inflate layout only if it's null and determine type using getItemViewType.
Look at this tutorial for further information.
To achieve some optimizations in structure that you've described in comment I would suggest:
Storing views in object called ViewHolder. It would increase speed because you won't have to call findViewById() every time in getView method. See List14 in API demos.
Create one generic layout that will conform all combinations of properties and hide some elements if current position doesn't have it.
I hope that will help you. If you could provide some XML stub with your data structure and information how exactly you want to map it into row, I would be able to give you more precise advise. By pixel.
I know how to create a custom row + custom array adapter to support a custom row for the entire list view. But how can one listview support many different row styles?
You already know the basics. You just need to get your custom adapter to return a different layout/view based on the row/cursor information being provided.
A ListView can support multiple row styles because it derives from AdapterView:
An AdapterView is a view whose children are determined by an Adapter.
If you look at the Adapter, you'll see methods that account for using row-specific views:
abstract int getViewTypeCount()
// Returns the number of types of Views that will be created ...
abstract int getItemViewType(int position)
// Get the type of View that will be created ...
abstract View getView(int position, View convertView, ViewGroup parent)
// Get a View that displays the data ...
The latter two methods provide the position so you can use that to determine the type of view you should use for that row.
Of course, you generally don't use AdapterView and Adapter directly, but rather use or derive from one of their subclasses. The subclasses of Adapter may add additional functionality that change how to get custom layouts for different rows. Since the view used for a given row is driven by the adapter, the trick is to get the adapter to return the desired view for a given row. How to do this differs depending on the specific adapter.
For example, to use ArrayAdapter,
override getView() to inflate, populate, and return the desired view for the given position. The getView() method includes an opportunity reuse views via the convertView parameter.
But to use derivatives of CursorAdapter,
override newView() to inflate, populate, and return the desired view for the current cursor state (i.e. the current "row") [you also need to override bindView so that widget can reuse views]
However, to use SimpleCursorAdapter,
define a SimpleCursorAdapter.ViewBinder with a setViewValue() method to inflate, populate, and return the desired view for a given row (current cursor state) and data "column". The method can define just the "special" views and defer to SimpleCursorAdapter's standard behavior for the "normal" bindings.
Look up the specific examples/tutorials for the kind of adapter you end up using.
Take a look in the code below.
First, we create custom layouts. In this case, four types.
even.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#ff500000"
android:layout_height="match_parent">
<TextView
android:id="#+id/text"
android:textColor="#android:color/white"
android:layout_width="match_parent"
android:layout_gravity="center"
android:textSize="24sp"
android:layout_height="wrap_content" />
</LinearLayout>
odd.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#ff001f50"
android:gravity="right"
android:layout_height="match_parent">
<TextView
android:id="#+id/text"
android:textColor="#android:color/white"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:textSize="28sp"
android:layout_height="wrap_content" />
</LinearLayout>
white.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#ffffffff"
android:gravity="right"
android:layout_height="match_parent">
<TextView
android:id="#+id/text"
android:textColor="#android:color/black"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:textSize="28sp"
android:layout_height="wrap_content" />
</LinearLayout>
black.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#ff000000"
android:layout_height="match_parent">
<TextView
android:id="#+id/text"
android:textColor="#android:color/white"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:textSize="33sp"
android:layout_height="wrap_content" />
</LinearLayout>
Then, we create the listview item. In our case, with a string and a type.
public class ListViewItem {
private String text;
private int type;
public ListViewItem(String text, int type) {
this.text = text;
this.type = type;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
After that, we create a view holder. It's strongly recommended because Android OS keeps the layout reference to reuse your item when it disappears and appears back on the screen. If you don't use this approach, every single time that your item appears on the screen Android OS will create a new one and causing your app to leak memory.
public class ViewHolder {
TextView text;
public ViewHolder(TextView text) {
this.text = text;
}
public TextView getText() {
return text;
}
public void setText(TextView text) {
this.text = text;
}
}
Finally, we create our custom adapter overriding getViewTypeCount() and getItemViewType(int position).
public class CustomAdapter extends ArrayAdapter {
public static final int TYPE_ODD = 0;
public static final int TYPE_EVEN = 1;
public static final int TYPE_WHITE = 2;
public static final int TYPE_BLACK = 3;
private ListViewItem[] objects;
#Override
public int getViewTypeCount() {
return 4;
}
#Override
public int getItemViewType(int position) {
return objects[position].getType();
}
public CustomAdapter(Context context, int resource, ListViewItem[] objects) {
super(context, resource, objects);
this.objects = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
ListViewItem listViewItem = objects[position];
int listViewItemType = getItemViewType(position);
if (convertView == null) {
if (listViewItemType == TYPE_EVEN) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_even, null);
} else if (listViewItemType == TYPE_ODD) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_odd, null);
} else if (listViewItemType == TYPE_WHITE) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_white, null);
} else {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_black, null);
}
TextView textView = (TextView) convertView.findViewById(R.id.text);
viewHolder = new ViewHolder(textView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.getText().setText(listViewItem.getText());
return convertView;
}
}
And our activity is something like this:
private ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // here, you can create a single layout with a listview
listView = (ListView) findViewById(R.id.listview);
final ListViewItem[] items = new ListViewItem[40];
for (int i = 0; i < items.length; i++) {
if (i == 4) {
items[i] = new ListViewItem("White " + i, CustomAdapter.TYPE_WHITE);
} else if (i == 9) {
items[i] = new ListViewItem("Black " + i, CustomAdapter.TYPE_BLACK);
} else if (i % 2 == 0) {
items[i] = new ListViewItem("EVEN " + i, CustomAdapter.TYPE_EVEN);
} else {
items[i] = new ListViewItem("ODD " + i, CustomAdapter.TYPE_ODD);
}
}
CustomAdapter customAdapter = new CustomAdapter(this, R.id.text, items);
listView.setAdapter(customAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView adapterView, View view, int i, long l) {
Toast.makeText(getBaseContext(), items[i].getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
now create a listview inside mainactivity.xml
like this
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.example.shivnandan.gygy.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/listView"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="100dp" />
</android.support.design.widget.CoordinatorLayout>
In your custom array adapter, you override the getView() method, as you presumably familiar with. Then all you have to do is use a switch statement or an if statement to return a certain custom View depending on the position argument passed to the getView method. Android is clever in that it will only give you a convertView of the appropriate type for your position/row; you do not need to check it is of the correct type. You can help Android with this by overriding the getItemViewType() and getViewTypeCount() methods appropriately.
If we need to show different type of view in list-view then its good to use getViewTypeCount() and getItemViewType() in adapter instead of toggling a view VIEW.GONE and VIEW.VISIBLE can be very expensive task inside getView() which will affect the list scroll.
Please check this one for use of getViewTypeCount() and getItemViewType() in Adapter.
Link : the-use-of-getviewtypecount
ListView was intended for simple use cases like the same static view for all row items.
Since you have to create ViewHolders and make significant use of getItemViewType(), and dynamically show different row item layout xml's, you should try doing that using the RecyclerView, which is available in Android API 22. It offers better support and structure for multiple view types.
Check out this tutorial on how to use the RecyclerView to do what you are looking for.

Categories

Resources