Finding out the parent of clicked element in android ListView - java

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
}
});
}

Related

Spinner updating only on user actions

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()

Dynamically adding view or inflating layout to RecyclerView's item

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

Android - Spinner setting TextView visible/invisible

I'm trying to do my first Spinner, and I have encountered some difficulties, such as that I don't know if I can get an option by spinner.getSelectItem == "some string".
Take a look at my code so far
Populating the spinner:
public void addItemsOnSpinner() {
Spinner buttonSpinner = (Spinner) findViewById(R.id.buttonSpinner);
List<String> list = new ArrayList<String>();
list.add("Ultimos 5 lancamentos");
list.add("Ultimos 7 lancamentos");
list.add("Ultimos 10 lancamentos");
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, list);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
buttonSpinner.setAdapter(dataAdapter);
}
Trying to make an if statement:
if(buttonSpinner.getSelectedItem().toString() == "Ultimos 10 lancamentos"){
textView.setVisibility(View.VISIBLE);
}
TextView code as requested:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Deposito"
android:visibility="invisible"
android:id="#+id/textView"
android:layout_row="2"
android:layout_column="0"
android:layout_gravity="center|left" />
And its code on the class:
TextView textView = (TextView)findViewById(R.id.textView);
Yes you can do it and it will work fine, but please use
buttonSpinner.getSelectedItem().toString().equals("Ultimos 10 lancamentos");
As Stefano has pointed out, your comparison should be using equals (which compares the String contents, vs == which compares the object references).
Otherwise your if statement should work, however its not clear where you are calling it from (and that might be the cause of the problem). If you want to make the comparison immediately after a spinner item is selected then you need to set an OnItemSelectedListener and make the comparison there.
Here is an example of how you might declare this listener inline:
buttonSpinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener()
{
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
String selectedItem = parent.getSelectedItem().toString();
if (selectedItem.equals("Ultimos 10 lancamentos"))
{
textView.setVisibility(View.VISIBLE);
}
}
public void onNothingSelected(AdapterView<?> parent)
{
}
});

Keep state of selected elements when data are changing in ListView

I have an activity with a list of Products (like a shopping list), in which we can choose the number of product units.
The activity contains EditText with TextWatcher and a ListView with a personal Adapter (extends BaseAdapter).
When I type a key in the EditText, the listView of products is refreshed with the new data.
But I would like to keep the state of my selected data.
For example, I type a reference in the EditText, then the listView contains 3 datas (A, B, C), I choose A, and type 5 units of it, then I type a new reference in the EditText (A and B disappears).
The listView contain only C, but I remove my search and go back to A, B, C.
the problem, the product A is unselected and have 0 of units (the 5 units have disappeared).
I would like to keep the selected products and the units even if I change my search in the EditText.
How to do that please ?
Here is a code snippet :
The layout of the activity :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:id="#+building_list/search_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Filter by reference"
android:inputType="textAutoCorrect|none|textAutoComplete|none|textNoSuggestions"
android:maxLines="1"
android:textSize="20dp" />
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="10dp"
android:id="#+id/productListView">
</ListView>
</LinearLayout>
The Adapter :
public class AdapterProduct extends BaseAdapter {
private List<Product> lstProduct;
private LayoutInflater mInflater;
public AdapterProduct(Context context, List<Product> listP) {
lstProduct = listP;
mInflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent) {
CheckedLinearLayout layoutItem;
if (convertView == null) {
layoutItem = (CheckedLinearLayout) mInflater.inflate(R.layout.row_product, parent, false);
final ViewHolderProduct viewHolder;
viewHolder = new ViewHolderProduct();
viewHolder.btPlusAmount = (Button)layoutItem.findViewById(R.id.btPlusAmount);
viewHolder.btPlusAmount.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Product p = (Product) viewHolder.product.getTag();
p.setAmount(p.getAmount()+1);
notifyDataSetChanged();
}
});
}
else {
layoutItem = (CheckedLinearLayout) convertView;
((ViewHolderProduct) layoutItem.getTag()).btPlusAmount.setTag(lstProduct.get(position));
}
ViewHolderProduct viewHolder = (ViewHolderProduct) layoutItem.getTag();
viewHolder.amount.setText(String.valueOf(lstProduct.get(position).getAmount()));
}
static class ViewHolderProduct {
protected TextView amount;
protected Button btPlusAmount;
}
}
And the AsyncTask with TextWatcher :
AsyncTask
doInBackground =>
modelProduct = new AdapterProduct(leContext,
ProductJSON.getService().searchProducts(leContext, url, params[0])); //params[0] = the text of EditText
private TextWatcher filterTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
//AsyncTask task
task.execute(s.toString());
}
};
I let you check my answer here.
1) Basically, add a new attribute to your adapter. A simple int[] is enough. Store the number of products selected inside.
2) Each time you build the View of a row of your ListView, check the number of products selected.
3)Make sure to update the number of products selected when you click on the button. Also, make sure to resize/update this array when your List changes. Call notifyDataSetChanged() Each Time you click on a product.

Custom Array Adapter Returning Blank Screen?

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.

Categories

Resources