I have been developing my first Android app the past days, using this guide: Material Design Guide from Google.
I have decided to go for the Tile fragments as my choice, but the problem is that the content in these tiles are static / the content in tile 1 is the same as in tile 2, 3, 4 and so on.
How do I change this so that each tile has unique content?
Any help would be greatly appreciated!
Normally you would have some data that you would want to present in this tiled list format. This would normally be passed into the ContentAdapter so that you can use it to fill each tile in. At the minute all the content is being set in XML, not in your adapter.
If you want to change the images for each you need to add an id attribute to the item_tile layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:padding="#dimen/tile_padding">
<ImageView
!--Add the id for the ImageView-->
android:id="#+id/tile_image"
android:layout_width="match_parent"
android:layout_height="#dimen/tile_height"
android:scaleType="centerCrop"
android:src="#drawable/paris" />
...
</RelativeLayout>
Then you should change the ViewHolder class in the TileContentFragment so that we can get hold of the ImageView and TextView in the item_tile layout.
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView tileImage;
TextView tileTitle;
public ViewHolder(View itemView) {
super(itemView);
this.tileImage = (ImageView) itemView.findViewById(R.id.tile_image);
this.tileTitle = (TextView) itemView.findViewById(R.id.tile_title);
}
}
Then just for example purposes lets set each tile's title to "Hello":
public static class ContentAdapter extends RecyclerView.Adapter<ViewHolder> {
Other class methods...
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tile, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.tileTitle.setText("Hello");
//If you had images for the different tile you could set them here too
//holder.tileImage.setImageResource([THE ID OF YOUR IMAGE HERE])
}
}
Hope this helps.
Related
I want to remove textview from specific items in recycler view. I wrote the below code to do that
#Override
public void onBindViewHolder(#NonNull final Myholder holder, final int possion) {
final String n = names.get(possion);
if(possion==3){
holder.textView.setVisibility(View.GONE);}}
but this changes all items and i want to make gone only position number 3 item view.
this is my view holder
public static class Myholder extends RecyclerView.ViewHolder {
TextView textView;
public Myholder(#NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textfolder);
}
}
what should i do?
Always remember that RecyclerView recycles view holders. That is, the same ViewHolder instance will be re-used for different views. This means that it is almost always a bad idea to have an if statement that modifies a view withouth having a corresponding else.
So, try this instead:
if (possion == 3) {
holder.textView.setVisibility(View.GONE);
} else {
holder.textView.setVisibility(View.VISIBLE);
}
Note also that just checking the position argument is not necessarily a good idea. If you use notifyItemInserted() or notifyItemRemoved(), this can lead to problems.
It would be better to set something about the item at that position to indicate that its text should not be shown.
My DB is like this.
I will show you DATA when I search my name on SearchView.
This is the layout of the Adapter.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="#+id/nameText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_green_light"
android:gravity="center"
android:text="Name"
android:textColor="#android:color/white"
android:textSize="30sp" />
<TextView
android:id="#+id/dataText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_light"
android:gravity="center"
android:text="Data No."
android:textColor="#android:color/white"
android:textSize="20sp" />
</LinearLayout>
One nameText and one dataText.
If I search for JOHN1, I wish he would appear like this.
Like GridView.
JOHN1 JOHN1 JOHN1 JOHN1
10 20 30 40
But I don't know what to do with Adapter's onBindViewHolder.
It's Adapter.
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
List<Data> dataList;
Context context;
public Adapter(List<Data> dataList, Context context) {
this.dataList = dataList;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
// holder.nameText.setText(dataList.get(position).getName());
// holder.dataText.setText(dataList.get(position).getData1());
// holder.dataText.setText(dataList.get(position).getData2());
// holder.dataText.setText(dataList.get(position).getData3());
// holder.dataText.setText(dataList.get(position).getData4());
}
#Override
public int getItemCount() {
return dataList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView nameText, dataText;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
nameText = itemView.findViewById(R.id.nameText);
dataText = itemView.findViewById(R.id.dataText);
}
}
}
Plz help me...
The table like structure is a very old pattern and is not accepted as per the design guidelines. Suggestion is to use a single card with multiple textviews whose data can be set using the viewHolder in onBindViewHolder method.
Check this link for Cards
Check this link for how to create a card-based layout
You can use a Staggered or a Grid Layout Manager for your RecyclerView which would give you a good look for showing the data.
You can check both styles in this article
Now if all this does not convince you and you want to stick to the design that you mentioned, then you can have a ViewHolder with LinearLayout and horizontal orientation in which you can add the view with title and description dynamically by looping over the object and setting the title and description values. Title would be repetitive in this case and use unnecessary space on the screen as per the design. So it is not recommended to go with this design.
Let me know if this helps.
you can use Multi Type holders
good sample is here:
sample
To use RecyclerView like GridView:
mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), NUMBER_OF_COLUMN));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mAdapter);
But in your case, I saw your database is stored like 1 object response contains name, data1, data2, data3, data4 fields.
{
"response":{
"name":"John",
"data1":10,
"data2":20,
"data3":30,
"data4":40
}
}
If you want display as
JOHN1 JOHN1 JOHN1 JOHN1
10 20 30 40
DONT use Gridlayout. Gridlayout should be use in case your data is like
{
"response":[
{
"name":"John",
"data":10
},
{
"name":"John",
"data":20
},
{
"name":"John",
"data":30
},
{
"name":"John",
"data":40
}
]
}
Of course you can convert your object response to array object like below json, but it isn't necessary. Just use RecyclerView as normal is better idea
I am using Glide to load URL into ImageView. My main problem is that when I scroll it seems like the sizing of the images are getting messed up which is causing some distortion. My relevant code is below.
XML (just the part relating to the ImageView):
<LinearLayout
android:id="#+id/image_holder_layout"
android:layout_width="85dp"
android:layout_height="85dp"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:gravity="center">
<ImageView
android:id="#+id/flyer_item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:background="#android:drawable/dialog_holo_light_frame" />
</LinearLayout>
RecyclerAdapter:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ItemHolder> {
private ArrayList<Item> items;
Context context;
public RecyclerAdapter(Context context, ArrayList<Item> items) {
this.items = items;
this.context = context;
}
#Override
public ItemHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
boolean shouldAttachToParentImmediately = false;
View view = LayoutInflater.from(context).inflate(R.layout.list_row_flyer, viewGroup, shouldAttachToParentImmediately);
ItemHolder viewHolder = new ItemHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ItemHolder holder, int position) {
holder.bind(position);
}
#Override
public int getItemCount() {
return items.size();
}
class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView itemImage;
public ItemHolder(View view) {
super(view);
this.itemImage = (ImageView) view.findViewById(R.id.flyer_item_image);
}
void bind(int listIndex) {
Glide.with(context)
.load(items.get(listIndex).getImageUrl())
.placeholder(R.drawable.placeholder_flyer_item_image)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(itemImage);
}
}
}
I had the same problem, images sometimes was resized to placeholder size which was quite different and images looked like stretched, so if Your images have different proportions then placeholder just remove placeholder or change it to fit the same size like images, but placeholder works best if images have similar proportions, alternative solution for placeholder is to set background in parent view element to avoid this problem and have some loading background.
I see also that You have wrap_content on ImageView size properties, I would suggest to set some width and height then glide can adjust images to that easily by crop or fit.
The list items are being reused, and when you scroll down you bind a larger image which makes the ImageView layout taller (wrap_content). When you scroll back up Glide sees that taller size and tries to load an image that has that size. wrap_content only works if the didn't have a layout before, otherwise Glide reads the laid-out width/height and uses that as the target size. I usually recommend a holder.imageView.layout(0,0,0,0) to reset the size of the view/target and behave as if the list item was just inflated.
I have taken an example code for implementing a RecyclerView, but trying to transpose it to work in a Child Fragment in my app.
The code is under 'Create Lists - examples'
Creating Lists and Cards
I run into trouble with my adapter..
public static class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView mTextView;
public ViewHolder(TextView v) {
super(v);
mTextView = v;
}
}
> #Override
> public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
> int viewType) {
> View v = LayoutInflater.from(parent.getContext())
> .inflate(R.layout.my_text_view, parent, false);
> ViewHolder vh = new ViewHolder(v);
> return vh;
> }
First, it doesn't build, complaining that I am calling constructor ViewHolder with a View, when the constructor is expecting a TextView.
Looking at the code, I agree!
But this is an official example so it must be right?
So what is different to my version compared with the example?
Two things that I can think of...
1)
the layout my_text_view is not given in the example, so I made my own. Did I make it right?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/t_title"
android:title="title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
2)
I am calling the adapter from my child fragment, and the example was not written with fragments in mind.
I've probably bitten off more than I can understand there.
Still, working this through, as far as i understand.
The 'parent' coming into my OnCreateViewHolder, is my RecyclerView ?
'v' should be the LinearLayout of my 'my_text_view' ?
'ViewHolder' should end up being a class with property mTextView equaling the TextView from my xml.
I just don't see how I go from v=LinearLayout , to TextView??
Anyone like to help explain to this noob ??
That example is not very good. It looks like two different projects spliced together. It should be something like this:
public class MyAdapter extends RecyclerView.Adapter {
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View v) {
super(v);
mTextView = (TextView) v.findViewById(R.id.t_title);
}
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
}
To explain this segment:
onCreateViewHolder is the method that will be called any time the RecyclerView needs to create a new type of ViewHolder. This may get called only a dozen or so times to get enough Views to cover the screen or it may be called quite a few times if you have many view types.
The parameter parent in this case will be the RecyclerView that accompanies it. Why not just make it RecycerView? Because Google designers decided it should be a ViewGroup. Also it's an Adapter pattern so the only guarantee you're supposed to have is that it's a ViewGroup (i.e., may not be a RecyclerView so you shouldn't build the Adapter with that assumption). Realistically, it will pretty much always be a RecyclerView though.
The parameter int viewType is to tell you what kind of View you're building. This is determined if you override getItemViewType() method of the Adapter. You don't need to worry about this if you only have one type of View though.
For the ViewHolder, this is to basically cache the different types of Views in your layout. These can be ImageView, TextView, etc. These will be "recycled" constantly as the user scrolls so you're not always creating them. That way, you only have to update the Views with the current information. In this case, there's just a title. The ViewHolder passed in bindViewHolder will be where the actual updating happens. This is called all the time, so there's no need to initialize the Views in onCreateViewHolder. Just need to create them.
I have the following scenario: I have a LinearLayout on which I then add "cards" to which is a custom class which extends LinearLayout.
The problem is that each card contains an image. Now if I have too many cards to display I get an out of memory error because of the size of the images.
How can I dynamically see which cards are currently displayed on the screen and only load the images of those cards and keep the rest null?
I am struggling to detect which card is currently displayed on the screen and which ones are not. And then also to have an event load and clear images as the user scrolls though the list.
You have to implement a RecyclerView, which does the job for you.
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
final Adapter adapter = new Adapter();
recyclerView.setAdapter(adapter);
The adapter:
private class Adapter extends RecyclerView.Adapter<MyViewHolder> {
#Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_main, viewGroup, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(final MyViewHolder myViewHolder, int i)
// set the content of the card
}
#Override
public int getItemCount() {
return // number of cards
}
}
The ViewHolder
private class MyViewHolder extends RecyclerView.ViewHolder {
public TextView text;
public TextView text2;
public ImageView imageView;
public MyViewHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(/* your textView */);
text2 = (TextView) itemView.findViewById(/* another textView */);
imageView = (ImageView) itemView.findViewById(/* an image */);
}
}
The Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:design="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivityFragment">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view"
/>
</RelativeLayout>
For something like this, you should probably use a Recycler View. This way you can recycle the views and ideally not run into memory issues and not have to have hacky solutions that check what is on the screen and what isn't.