I am working on a social network application in which I need to show feeds to subscribers.. for showing feeds in a list. I am using RecyclerView. No Problem till now... but the problem comes when I have to show some comments on the feed. I am trying to accomplish a layout similar to Instagram where they show an image and maximum 3-4 comments with it in the main list. I am getting Image links and its comments (in JSON array form) from server.. sometimes I get comment array size 0 and sometime 1/2../10. I have created an XML for the comment layout that contains 2 TextView and I am inflating it in FeedViewHolder based on my ViewType.. my item_comment look like
<RelativeLayout
android:id="#+id/comment_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingTop="5dp">
<TextView
android:textStyle="bold"
android:textColor="#color/indigo_500"
android:id="#+id/comment_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="#drawable/profile_pic"
android:text="Somesh Kumar"/>
<TextView
android:id="#+id/comment_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/comment_name"
android:ellipsize="end"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="This is a comment"/>
</RelativeLayout>
I am passing my feed list to RecyclerView adaptor..and then I am checking if the item (that will be shown) should have the comments or not.
#Override
public int getItemViewType(int position)
{
FeedParser.DataClass dataClass = feedList.get(position);
int ITEM_TYPE = 0;
if (dataClass.getPost_type() == FEED_TYPE_PICTURE) // Feed item contains caption with pictures
{
// check if it has comments
if (dataClass.getComments().size() == 0)
{
ITEM_TYPE = PICTURE_WITHOUT_COMMENTS;
}
else
{
ITEM_TYPE = PICTURE_WITH_COMMENTS;
}
}
return ITEM_TYPE;
}
and passing ITEM_TYPE to onCreateViewHolder like
#Override
public FeedViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_feed, parent, false);
return new FeedViewHolder(itemView, viewType);
}
and in FeedViewHolder inflating item layout and comment layout if viewType has comments
public FeedViewHolder(View itemView, int viewType)
{
super(itemView);
tvFeedUsername = (TextView) itemView.findViewById(R.id.tvFeedUsername);
tvFeedCaption = (TextView) itemView.findViewById(R.id.tvFeedCaption);
switch (viewType)
{
// TODO: Write code for other feed type such as picture and video
// viewType 1 = PICTURE_WITH_COMMENTS
case 1:
layoutComments = (LinearLayout) itemView.findViewById(R.id.layoutComments);
commentView = layoutInflater.inflate(R.layout.item_comment, null);
postCommentText = (TextView) commentView.findViewById(R.id.comment_text);
postCommentName = (TextView) commentView.findViewById(R.id.comment_name);
layoutComments.addView(commentView);
break;
}
}
I have verified this works perfect but when i setText for holder.postCommentName & holder.postCommentText in onBindViewHolder it only set text for last comment ( i know it's because i am looping through all the comment and setting them one by one on the same holder.postCommentText and holder.postCommentName here is my onBindViewHolder..
#Override
public void onBindViewHolder(FeedViewHolder holder, int position)
{
FeedParser.DataClass feedModel = feedList.get(position);
holder.tvFeedUsername.setText(feedModel.getName());
holder.tvFeedCaption.setText(feedModel.getPost_status());
if (getItemViewType(position) == TEXT_WITH_COMMENTS)
{
for (int commentNum = 0; commentNum < feedModel.getComments().size(); commentNum++)
{
holder.postCommentName.setText(feedModel.getComments().get(commentNum).getUser_name());
holder.postCommentText.setText(feedModel.getComments().get(commentNum).getComment());
}
}
}
I don't know even if this is the right approach or not... but this is how some people wrote an answer here... like This one . I have searched many SO post but not getting what I want. Is there any other way where I can inflate comment's XML layout and add it multiple time on RecyclerView 's item?
Thanks any help would be much appreciated!
i Believe you need to create a nested recycler view in the main recycler view and you need to pass the data into the nested adapter as that of the comments .So basically
1.)Main Recycler View contains Caption , Image , RecyclerView for comments (as per instagram)
2.)In the second reyclerview put your above comment layout and create a seperate adapter with data as comments
Thats It .. Ask me for any further help
Related
I have IconText that has image and text views inside it, both class and xml.
I then populate the Spinner inside of the MainActivity with these IconTexts, using extended BaseAdapter (IconTextAdapter) as adapter.
Now, IconText works fine (shows as it should).
Spinner however doesn't.
When I start the app, it shows the first IconText as it should.
When I open the choose dialog, everything is showing as it should.
When I select another item, the choose dialog collapses and spinner displays no IconText (ie. only "arrow down" for opening choose dialog).
I can still open the choose dialog and choose another.
I've noticed that if I exit from app (return button, not really quiting the app) and enter again that the proper IconText is shown.
I guess that the fault lies with the adapter?
I will try avoiding posting a lot of code [:
IconText.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView android:id="#+id/it_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/home"/>
<TextView
android:id="#+id/it_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="20sp"
android:text="ABC"
android:textColor="#color/black_overlay"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
IconText.java
public class IconText {
static LayoutInflater inflator;
public ImageView icon;
public TextView text;
public View view;
public IconText(String title, int icon_id){
Log.d("IconText", "Create");
view = inflator.inflate(R.layout.icon_text, null);
text = (TextView) view.findViewById(R.id.it_text);
icon = (ImageView) view.findViewById(R.id.it_image);
text.setText(title);
icon.setImageResource(icon_id);
}
public static void initInflator(Context context){
if(inflator != null) return;
Log.d("IconText", "Init inflator");
inflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
}
IconTextAdapter.java
public class IconTextAdapter extends BaseAdapter implements SpinnerAdapter{
public ArrayList<IconText> items;
public IconTextAdapter(){
super();
items = new ArrayList<>();
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int arg0) {
return items.get(arg0);
}
#Override
public long getItemId(int position) {
return items.get(position).view.getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return items.get(position).view;
}
}
MainActivity, relevant code
void initFilter(){
Log.d("MainAction", "Find Filter");
filter = (Spinner) findViewById(R.id.filter);
Log.d("MainAction", "Create Adapter");
IconTextAdapter adapter = new IconTextAdapter();
Log.d("MainAction", "Create items");
for(int i=0; i<ETypes.names.length; i++){
IconText it = new IconText(ETypes.names[i], ETypes.icons[i]);
adapter.items.add(it);
}
Log.d("MainAction", "Setting adapter");
filter.setAdapter(adapter);
}
ETypes names[string] and icons[id] are static.
Inflator is initialized succesfully.
I haven't worked much in Android. If it were Java/Swing, I guess I would just call a redraw or something.
I know there are some bad practices here (all public variables, and so on). This code is still in early prototype stage, it will be fixed soon. I'm not looking for optimization, just for the solution to my problem.
Update 1:
So I saw I didn't implement getDropDownView so I did, the same code as getView (no need to post it?).
Also I made an experiment: At the end of IconText contrusctor I added
view = text
And it works just fine (showing only text).
I guess this pinpoints that the problem originates from custom view?
Update 2:
Did another experiment with IconText, setting view = icon; and it doesn't behave as it should, ie. it behaves like it's a custom view.
Doesn't really solve this specific bug, but it solves my problem.
Custom Image and Text View in Spinner Solution.
To update the list you must call
adapter.notifyDataSetChanged()
after adding new data to your adapter
Please refer to the following
https://developer.android.com/reference/android/widget/BaseAdapter.html#notifyDataSetChanged()
I'm building my first app based on material from http://javatechig.com/video/json-feed-reader-in-android.
Everything goes ok so far, but I found one bug with ListView elements, which I can not manage to resolve by myself :(
I have extended list_row_layout.xml by 2 fields:
<Button
android:layout_width="wrap_content"
android:layout_height="20dp"
android:text="komcie"
android:textSize="11sp"
android:id="#+id/loadComments"
android:layout_gravity="center|bottom"
android:background="#bbb"
android:layout_marginLeft="5dp"
android:enabled="true"
android:clickable="true"
android:onClick="clickedLoadComments"
android:elegantTextHeight="true"
android:layout_toRightOf="#id/thumbImage"
android:layout_below="#+id/content"
android:padding="1px" />
<ListView
android:id="#+id/comment_list"
android:layout_toRightOf="#id/thumbImage"
android:layout_below="#+id/content"
android:paddingTop="5dp"
android:layout_marginTop="0dp"
android:paddingLeft="5dp"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:cacheColorHint="#00000000"
android:dividerHeight="1dp"
android:focusable="false"
android:listSelector="#drawable/list_selector_flatcolor"
android:visibility="invisible" />
Button.android:onClick="clickedLoadComments" function load Json with elements into ListView/comment_list. It works quite fine. But if there are more elements than could be displayed on screen (~8 elements) there is a bug. Comments from clicked element are loaded into every 8th element in a ListView.
Some code:
public void clickedLoadComments(View v)
{
try {
View parent = (View)v.getParent();
ViewHolder t = (ViewHolder) parent.getTag();
if( parent != null ) {
this.loadCommentsForLeaf(parent);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
protected void loadCommentsForLeaf( View view )
{
String tmpUrl = "http://some.url.com/Ajax/LoadComments?lid=" + this.currentLeafInUse;
JSONObject commentsJson = this.getJSONFromUrl(tmpUrl);
this.parseJsonComments(commentsJson);
if( commentsJson != null )
this.updateCommentList(view);
}
public void updateCommentList( View view) {
commentListView = (ListView) view.findViewById(R.id.comment_list);
commentListView.setVisibility(View.VISIBLE);
CommentListAdapter cla = new CommentListAdapter(this, this.commentList.get(this.currentLeafInUse));
commentListView.setAdapter(cla);
// Set list height.
ViewGroup.LayoutParams params = commentListView.getLayoutParams();
params.height = setListViewHeightBasedOnItems(commentListView) + 20;
commentListView.setLayoutParams(params);
commentListView.requestLayout();
}
CustomListAdapter.java code is mostly the same as the one in tutorial.
I would really appreciate help as I have spent many hours figuring it out with not success :(
This is just a guess. You might post your Adapter code and your parseJsonComments also if this does not work.
The Cause:
The problem you are describing might be caused due to the recycling and the reusage of Views. Take a look at this image from http://android.amberfog.com
As you can see the 1. items is reused and becomes the 8. item after scrolling.
Let's assume that Item 1 has an OnClickListener which updates a Text of the item.
For example we set the text to "clicked" after the OnClickListener is triggered.
Because item 1 is reused to create item 8, item 8 will also display the text "clicked".
The Solution:
The usual way is to save all states/content in a List(or whatever) and update everything in the getView call. So if you want to update text:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
...
holder.textView.setText(jsonTexts[position]);
...
return convertView;
}
And if you want to update an item just update the List in your Adapter which holds the content/JsonObjects(etc.) and call notifyDataSetChanged.
public void updateCommentList(JSONObject commentsJson, int position) {
// does not exist you might create something
//like that in your Adapter class
commentListAdapter.updateItem(commentsJson,position);
commentListAdapter.notifyDataSetChanged();
}
After i populate the listview i call this method:
private void registerClickCallback() {
ListView list = (ListView) findViewById(R.id.lv);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View viewClicked,
int position, long id) {
String xx = position+ ":" + id;
//then you can do what ever you want
}
});
}
I have a gridview full of images and the rows of a grid view align by columns like this
1_2_3
4_5_6
7_8_9
However I want to offset certain rows to get something like this
1_2_3_
_4_5_6
7_8_9_
So that the second row and an unknown number of additional rows are moved over to the right.
One solution is to make multiple gridviews and shift them over accordingly but that won't work when I have alot of rows. Another would be to somehow change the margins in the ImageAdapter using the position to get the row but I haven't been able to make that work without crashing. Is trying to edit the LayoutParams in the ImageAdapter even the right way to go about something like this?
You can use two types of views, with two layouts, for example:
in your adapter, you could implements:
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
if (isImageLeft(position)) {
return VIEW_TYPE_LEFT_IMAGE; //TODO: you should define it
} else {
return VIEW_TYPE_RIGHT_IMAGE;//TODO: you should define it
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null){
if (getItemViewType(int position) == VIEW_TYPE_LEFT_IMAGE){
view = inflater.inflate(R.layout.layout_image_left, null);//TODO: you should have set the reference of LayoutInflater
}else{
view = inflater.inflate(R.layout.layout_image_right, null);
}
}
//.......
}
and in those two layout file, you could set a LinearLayout (or other layout as you like), with ImageView inside with the left, or right margin respectively. for example:
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp" />
</LinearLayout>
I have a listview with a checkbox, an image and a text field. Now I do want to display more than one items in the text field. The problem is that I want the arguments in neat colums. Can I somehow set tabstops or a format or something like that in the text field or do I have to actually make more text fields and set each value in its own field ?
My list should look somewhat like:
[Image] [checkbox] "arg11 arg12 arg13"
[Image] [checkbox] "arg21 arg22 arg23"
[Image] [checkbox] "arg31 arg32 arg33"
Thanks for any advice.
Edit: Just to clarify things. I have a working listview/adapter with the image/checkbox and one textfield already. I am kind of new to Android so all I want to know if there is an elegant way to handle displaying several text items in the text field. In Windows I would simply use tabstopps and they would look ordered and in a column but I don't know my way well enough around android to know if there is something similar or if individual text fields are required to get the text fields in columns.
You create new xml say row.xml, which we can inflate inside listview for each row.
Design the row however you want, for ex:
row.xml
<LinearLayout android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal">
<ImageView ..../>
<CheckBox ...../>
<TextView ...../>
<TextView ...../>
</LinearLayout>
Then use CustomAdapter for your listview
like,
listView.setAdapter(new CustomAdapter());
CustomAdapter,
class CustomAdapter extends BaseAdapter{
publicCustomAdapter() {
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return xyz.size();
}
#Override
public String getItem(int position) {
return xyz.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.row, null);
holder = new ViewHolder();
holder.imageView = (ImageViewView)convertView.findViewById(R.id.image);
holder.checkbox = (CheckBox)convertView.findViewById(R.id.checkbox);
holder.textView1 = (TextView)convertView.findViewById(R.id.text1);
holder.textView2 = (TextView)convertView.findViewById(R.id.text2);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
//TODO set the values for views and return view
return convertView;
}
}
You should Re-work on the row xml. I think you may know about the row xml where you can chage the design of each and every row. Take a Relative or Linear Layout and add the views as shown below.
[Image] [checkbox] "text1" "text2 "text3"
[Image] [checkbox] "text1" "text2 "text3"
[Image] [checkbox] "text1" "text2 "text3"
Instead of taking tab spaces, take three different TextView's. keep '1' as weight for each and every TextView and add these to a LinearLayout which is having total weight sum as 3.So all the three TextView will be equally placed in the parent view.
Try this and lemme know whether you fixed it.....
Basically you need to implement your own adapter for your ListView.
First you need to create your own adapter class which either baseAdapter or their derivatives.And then you need to implement your ListView item display logic in it.
Like setting your textViews,imageViews etc.
Check this tutorial for more info.
http://www.vogella.de/articles/AndroidListView/article.html
From one of my earlier posts:
Whenever you want to do processing with the views in a ListView you
need to create a custom adapter that will handle your logic
implementation and pass that information to the views as necessary.
If you are sure about the sizes of text then you can use tabstops in your text. If that doesnt work then your are stuck with extending your row layout. as mentioned by Pavandroid
I'm still very new to application development, so this is probably a very stupid question but I can't seem to find the right answer (or at least one that I can understand with my very limited knowledge of java).
I'm using a custom ArrayAdapter called ListRow. It works fine with a regular Activity, but not with the ListActivity that I need it to be in for my app to work.
Below is a sample of the code that I'm using. Any help would be greatly appreciated and you'd be helping a ton!
ListView mListview;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ListRow(this, THEME_NAMES, THEME_ICONS));
getListView().setTextFilterEnabled(true);
}
public class ListRow extends BaseAdapter {
private Context mContext;
private String[] mThemeNames = THEME_NAMES;
private int[] mThemeIcons = THEME_ICONS;
public ListRow(Context c, String[] t, int[] i) {
mContext = c;
mThemeNames = t;
mThemeIcons = i;
mListview=(ListView)findViewById(R.id.list);
}
#Override
public int getCount() {
return mThemeNames.length;
}
#Override
public Object getItem(int arg0) {
return null;
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(int position, View converView, ViewGroup parent) {
View List;
if(converView==null){
List=new View(mContext);
LayoutInflater mLayoutinflater=getLayoutInflater();
List=mLayoutinflater.inflate(R.layout.list_view, parent, false);
} else {
List = (View)converView;
}
ImageView imageView = (ImageView)List.findViewById(R.id.image);
TextView textView = (TextView)List.findViewById(R.id.text);
imageView.setImageResource(mThemeIcons[position]);
textView.setText(mThemeNames[position]);
return List;
}
}
And here's the layout I've defined for each list item
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="center"
android:id="#+id/image"
android:layout_alignParentLeft="true"
android:contentDescription="#string/preview" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/image" />
</RelativeLayout>
If you can, please use small words with me lol, java has turned out to be hard to understand for me, and also try to explain as much as you can. Thanks in advance!
FIGURED IT OUT!
So I just put you all through a bit of hell. The layout that contains my list items is called list_item, not list_view. However I have learned a lot here so THANK YOU ALL VERY MUCH! I wish there were a way I could help you guys out...
Moral of this question? CHECK YOUR LAYOUT NAMES!!
You need to set The Adapter in this way
setListAdapter(new ListRow(this, your_theme_names_array, your_theme_icon_array));
You dont need to use ArrayAdapter for this, that is just for Creating a Adapter for an array of String
EDITED
The Layout XML does not have the problem i think.
Check the List given below one by one
Check List
Check Whether R.layout.list_view point to the layout you given in the Question.
Try this for setting adapter setListAdapter(new ListRow(this, String[] { }, int[] { })); it will show you blank screen (If you get the Blank Screen that means either THEME_NAMES or THEME_ICONS is null or their values is null)
Remove the Line imageView.setImageResource(mThemeIcons[position]); and
textView.setText(mThemeNames[position]); this will also give u blank screen (If you get blank screen then R.layout.list_view does not contain R.id.image or R.id.text.
You have to add your mListView in your ArrayAdapter in setListAdapter.Only then the contents of your listview will be display in the pattern you have mentioned in customadapter. I cannot see where you have added elements in listview.