adapter.notifyItemChanged(position) has no effect - android

For each item of the list ("itemList")
ArrayList<Item> itemList = new ArrayList<>();
shown by "recyclerView" that is controlled by "itemAdapter", its quantity is displayed, as well as a plus and minus button which allows the user to respectively increase and decrease the quantity.
In the set-up of "itemAdapter" these buttons are given functionality by:
itemAdapter.setOnItemClickListener(new ItemAdapter.OnItemClickListener() {
#Override
public void onPlusClick(int position) {
itemList.get(position).setQuantity(itemList.get(position).getQuantity() + 1);
itemAdapter.notifyItemChanged(position);
Log.d(TAG, "onPlusClick: position = " + position + " quantity = " + itemList.get(position).getQuantity());
}
#Override
public void onMinusClick(int position) {
itemList.get(position).setQuantity(itemList.get(position).getQuantity() - 1);
if (itemList.get(position).getQuantity() < 1) {
itemList.get(position).setQuantity(1);
}
itemAdapter.notifyItemChanged(position);
Log.d(TAG, "onMinusClick: position = " + position + " quantity = " + itemList.get(position).getQuantity());
}
})
The display of the quantity is set up in the onBindViewHolder() of the adapter class ItemAdapter with:
holder.textviewQuantity.setText(String.valueOf(currentItem.getQuantity()));
And this is the code for the adapter class
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemListHolder> {
private ArrayList<Item> mItemList;
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onPlusClick(int position);
void onMinusClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mListener = listener;
}
#NonNull
#Override
public ItemListHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new ItemListHolder(v, mListener);
}
#Override
public void onBindViewHolder(#NonNull ItemListHolder holder, int position) {
Item currentItem = mItemList.get(position);
holder.imageviewCategoryIcon.setImageResource(Main.getIconID(currentItem.getCategoryNumber()));
holder.textviewItemName.setText(currentItem.getItemName());
holder.textviewQuantity.setText(String.valueOf(currentItem.getQuantity()));
}
}
#Override
public int getItemCount() {
return mItemList.size();
}
public static class ItemListHolder extends RecyclerView.ViewHolder {
CardView cardviewItem;
ImageView imageviewCategoryIcon;
TextView textviewItemName;
Button buttonPlus;
Button buttonMinus;
TextView textviewQuantity;
public ItemListHolder(#NonNull View view, OnItemClickListener listener) {
super(view);
cardviewItem = view.findViewById(R.id.cardview_item);
imageviewCategoryIcon = view.findViewById(R.id.imageview_category_icon);
textviewItemName = view.findViewById(R.id.textview_item_name);
buttonPlus = view.findViewById(R.id.button_plus);
buttonMinus = view.findViewById(R.id.button_minus);
buttonPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
int position = getBindingAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onPlusClick(position);
}
}
}
});
buttonMinus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
int position = getBindingAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onMinusClick(position);
}
}
}
});
}
}
public ItemAdapter(ArrayList<Item> itemList) {
mItemList = itemList;
}
}
When the app starts up, the list of items is displayed, with each item showing the correct quantity.
But when the user clicks a plus or minus button, the quantity displayed doesn't change, though the logs clearly indicate the correct position in "itemList" has been chosen, and that the quantity of the corresponding item has indeed changed correctly.
It looks like itemAdapter.notifyItemChanged(position) has no effect. I also tried itemAdapter.notifyDataSetChanged(), but with no effect either. The recycler display is not being refreshed with the data changes.
What could be wrong?
PS: my very unworkable workaround is to run the whole recycler set-up each time that plus or minus button is clicked. Works fine if the list of items is no longer than the screen, but of course, resets to the top of the list if you press a button of an item that was found scrolling down.

This is an answer by the author.
The problem could ultimately be narrowed down to itemAdapter.notifyDataSetChanged() stalling when the entire "itemList" has been changed, like with a for loop. Also itemAdapter.notifyItemChanged() then become irresponsive. As workaround, I have everywhere replaced itemAdapter.notifyDataSetChanged() with setUpRecycler(), an own-defined method that sets up the whole RecyclerView again. Inevitably, the view will go to the top of the list, but that's a quirk I'm (gladly) prepared to accept. itemAdapter.notifyItemChanged() works fine then.

Related

how can i apply limit to recyclerview?

i want apply limit to recyclerview and only show 5 result in it and in the end of the list show a button for go to another place?!
my code in below;
adapter codes:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.Holder> {
ArrayList<Products> ProductsList;
Context context;
public ProductAdapter(ArrayList<Products> productsList, Context context) {
ProductsList = productsList;
this.context = context;
}
#NonNull
#Override
public Holder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.row_layout_horizental, parent, false);
return new Holder(v);
}
#Override
public void onBindViewHolder(#NonNull Holder holder, int position) {
Products products = ProductsList.get(position);
holder.txtName.setText(products.getName());
holder.txtPrice.setText(products.getPrice());
Picasso.get().load(Config.ip_value + "/images/" + products.getPhoto()).into(holder.imgV);
}
#Override
public int getItemCount() {
return ProductsList.size();
}
public class Holder extends RecyclerView.ViewHolder {
TextView txtName;
TextView txtPrice;
ImageView imgV;
public Holder(#NonNull View itemView) {
super(itemView);
txtName = itemView.findViewById(R.id.rowTxtProductName);
txtPrice = itemView.findViewById(R.id.rowTxtPrice);
imgV = itemView.findViewById(R.id.rowImgProduct);
}
}
}
i have some codes in main fragment but i dont think its necessary to put in this place but if you want to see them comment and i will put all in update text
thanks
You can either slice the list to contain only 5 items before passing it to RecyclerView or just change getItemCount method in Adapter to return 5
#Override
public int getItemCount() {
return ProductsList.size() > 5 ? 5 : ProductsList.size();
}
You can set limit as by using getItemCount() as
#Override
public int getItemCount() {
return 5;
}
by another place if u mean to other activity or fragment, then place
the button at the end of your recyclerview layout in your xml file.
for e.g : if using RelativeLayout,
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:id="#+id/recyclerview"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Next"
android:layout_alignParentBottom="true"
android:layout_below="#+id/recyclerview"
android:id="#+id/btn_next"/>
</RelativeLayout>
then on button click you can call the intent or similar to go to your page
You can set limit query in your ProductsList API.
if you are using retrofit2
you can define limit parametr in your API interface, if that option has in your backend server,
#GET("products")
Call<Products> getProducts(#Query("limit") int limit);
and define method in your activity class where you pass setAdapter, like below
public void getProducts(int limit){
Call<Products> call = yourAPI.getProducts(limit);
call.enqueue(new Callback<Products>() {
#Override
public void onResponse(Call<Products> call, Response<Products> response) {
if (response.isSuccessful() && response.body() != null) {
ArrayList<Products> products = response.body();
if (products == null) products = new ArrayList<>();
productsAdapter.setData(products); // setData needs to be defined in your adapter
} else {
Toast.makeText(yourActivity.this, "Server Error", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<Products> call, Throwable t) {
if(t.getMessage() != null) Log.e("ErrorTag", t.getMessage());
Toast.makeText(yourActivity.this, "Server Error", Toast.LENGTH_SHORT).show();
}
});
Don't forget to define new method called setData,
public void setData(ArrayList<Products> productsList) {
this.ProductsList.addAll(productsList);
notifyDataSetChanged();
};
And to get other 5 products I don't advise you to create next or prev button,
better add code below in your onBindViewHolder method of your adapter to make load another 5 more products when user scrolls to the last item,
if (position == (ProductsList.size() - 1)){
if (context instanceof YourActivity){
YourActivity activity = (YourActivity) context;
activity.getProducts(ProductsList.size());
}
}

How to get some data after click on certain item RecyclerView?

I have already seen a lot of material on this subject even here, namely the list of individual elements of the list. My application receives from the server two lists of messages, incoming and outgoing. And when you click on one of these two lists, you need to switch to the activation, which contains the selected message. When you press it, you need to somehow pull it out the way we said and throw this id into the query. I currently have such an adapter for RecyclerView:
package com.example.developer_4.test_login.Tabs;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.developer_4.test_login.R;
import com.example.developer_4.test_login.data.model.Message;
import java.util.List;
class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder> {
private RecyclerView recyclerViewItemClickListener;
private List<Message> messageList;
private Context ctx;
MessageAdapter(List<Message> messageList, Context ctx) {
this.messageList = messageList;
this.ctx = ctx;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_of_rec_m, viewGroup, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Message message = messageList.get(position);
String id = String.valueOf(message.getId());
holder.subject.setText(message.getSubject());
holder.from.setText(message.getSender_name());
holder.date.setText(message.getDate());
holder.getAdapterPosition();
}
#Override
public int getItemCount() {
return messageList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
final TextView from, subject, date;
int position = 0;
ViewHolder(View v) {
super(v);
subject = v.findViewById(R.id.subject);
from = v.findViewById(R.id.from);
date = v.findViewById(R.id.date);
//id = v.findViewById(R.id.id);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
}
}
at the moment I have hung on a click on the message element sending a request with the id of the message that I entered manually, I have a function getId in the response class from the server, that is, I can get this id, but I do not understand how to catch clicking on a separate list item . I've seen ways to use position but I somehow did not get accustomed to them)) after clicking on a certain element of this list, I need to pass on to another activity of the id of the message by which we clicked in the list, in order to display this message on the other screen.
Thank you all for valuable advice, answers and criticism)). Thank you in advance for your cooperation.
Just try setting :
holder.setTag(position);
then
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int pos = (int)view.getTag();
Message message = messageList.get(pos);
}
});
You can add data by adding tag to any view in your onBindViewHolder method.
holder.itemView.setTag("Some Data");
And then get that tag when clicked.
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String someData=(String)view.getTag();
}
});

Android: Implementing mopub native ads in recyclerview gives incorrect item positions and IndexOutOfBoundsException

In my android app I'm trying to implement mopub native ads. Before using these native ads my app was functioning correctly as expected. Now it gives outofbound exceptions and incorrect items when list item is clicked. I'm using recyclerview and using infinite scrolling, it gets item from my api and displays item correctly, but when clicking on item it gives incorrect item.
Here is my recyclerview adapter below.
package com.example.adapters;
import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.MyApplication;
import com.example.R;
import com.example.events.ErrorLoadingFeed;
import com.example.events.FeedLoaded;
import com.example.events.LoadMoreFeed;
import com.example.model.Post;
import com.example.model.Pagination;
import com.example.ui.InfoActivity;
import com.example.utils.MyAppConstants;
import com.google.gson.Gson;
import com.squareup.picasso.Picasso;
import java.util.List;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Header;
import retrofit.client.Response;
/**
* Created by starwar on 08/09/15.
*/
public class PostRecycleAdapter extends RecyclerView.Adapter<com.example.adapters.PostRecycleAdapter.PostViewHolder> implements Callback<List<Post>> {
private Context mContext;
private List<Post> mPosts;
// Allows to remember the last item shown on screen
private int lastPosition = -1;
// Pagination
private Pagination mPagination;
public PostRecycleAdapter(Context context, List<Post> posts) {
mContext = context;
mPosts = posts;
}
#Override
public PostViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
PostViewHolder viewHolder = new PostViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(PostViewHolder holder, int position) {
holder.bindPost(mPosts.get(position));
// Here you apply the animation when the view is bound
setAnimation(holder.mPostName, position);
//check for last item
if ((position >= getItemCount() - 1)) {
// Loading next set of list items on scroll
if (mPagination != null && !mPagination.getOutOfRange() && mPagination.getNextPage() != null){
MyApplication.bus.post(new LoadMoreFeed(mPagination));
}
}
}
#Override
public int getItemCount() {
return mPosts == null ? 0 : mPosts.size();
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate, int position)
{
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition)
{
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
public class PostViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView mPostName;
public ImageView mAnchorImage;
public PostViewHolder(View itemView) {
super(itemView);
mPostName = (TextView) itemView.findViewById(R.id.postName);
mAnchorImage = (ImageView) itemView.findViewById(R.id.anchorImage);
itemView.setOnClickListener(this);
}
public void bindPost(Post post) {
mPostName.setText(post.getName());
String postImage = post.getPostImage();
Picasso.with(mContext)
.load(postImage)
.placeholder(R.drawable.avatar_empty)
.into(mAnchorImage);
}
#Override
public void onClick(View view) {
int position = getAdapterPosition(); // gets item position
Post post = mPosts.get(position);
int postId = post.getId();
String postName = post.getName();
Intent intent = new Intent(mContext, InfoActivity.class);
intent.putExtra(MyAppConstants.POST_ID, postId);
intent.putExtra(MyAppConstants.POST_NAME, postName);
mContext.startActivity(intent);
}
}
#Override
public void success(List<Post> posts, Response response) {
if (mPosts == null) {
mPosts = posts;
} else {
mPosts.addAll(posts);
}
notifyDataSetChanged();
List<Header> headerList = response.getHeaders();
for(Header header : headerList) {
if ("X-Pagination".equals(header.getName())) {
Gson gson = new Gson();
mPagination = gson.fromJson(header.getValue(), Pagination.class);
}
}
MyApplication.bus.post(new FeedLoaded(mPagination));
}
#Override
public void failure(RetrofitError error) {
Log.d("Call", " : Failed => " + error);
MyApplication.bus.post(new ErrorLoadingFeed(error));
}
public void clearData() {
if (mPosts != null) {
mPosts.clear();
notifyDataSetChanged();
}
}
public List<Post> getPosts() {
return mPosts;
}
}
And I'm using guide given here to show ads
https://dev.twitter.com/mopub/native/native-android-sdk-integration
Please help. what I can/should do to resolve this.
Thanks in advance.
I know this is an old question, but I was looking for the answer for a couple of hours. Probably I'll save someone's time with this solution.
If you will look carefully documentation here: https://github.com/mopub/mopub-android-sdk/wiki/Native-Ads-with-Recycler-View you will find this:
If you register any data observers on your original Adapter, you should instead register them on the MoPubRecyclerAdapter so they will receive messages with the adjusted position of content items. If you do not do this, the positions you receive for insertion and removal messages may be inaccurate. Be sure to check isAd(position) and use MoPubRecyclerAdapter#getOriginalPosition if you need the position of the item in your local adapter.
So, to get real position, it is necessary to use MoPubRecyclerAdapter.getOriginalPosition(position_in_your_adapter) method.

How to mark views in a ListView?

I have an app with a list view. The listview works fine. The problem starts, when I want the list to start with some of the rows marked. I can mark a row, if I press on it. But, don't seem to find a way to mark any row on initialization.
This is my code:
listViewOfBluetooth = getListView();
setInitialEnabledDevices();
listViewOfBluetooth.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String chosenBluetoothDevice = (String) ((TextView) view).getText();
BluetoothEnableOrDisable(view, chosenBluetoothDevice);
Toast.makeText(getApplicationContext(), chosenBluetoothDevice, Toast.LENGTH_SHORT).show();
editor.putString("bluetooth_name_from_list1", chosenBluetoothDevice);
editor.putBoolean("have_the_cars_bluetooth", true);
editor.commit();
Intent intent = new Intent(List.this, ParkOGuardActivity.class);
startActivity(intent);
}
});
}
public static void setInitialEnabledDevices(){
int length = listViewOfBluetooth.getChildCount();
View view = null;
String first = prefs.getString("bluetooth_name_from_list0", "");
String second = prefs.getString("bluetooth_name_from_list1", "");
String third = prefs.getString("bluetooth_name_from_list2", "");
for(int i = 0; i < length; i++){
view = listViewOfBluetooth.getChildAt(i);
if(view.equals(first) || view.equals(second) || view.equals(third)) {
view.setBackgroundColor(Color.GRAY);
}
}
}
How can I fix it?
Thanks!
You can achive this by using custom adapter. Here is the workaround.
Initialize your custom adapter
Add some flag for marked device names.
Override the getView() & check for the flag. And set the background of the list item accordingly.
Reply if you don't get it or face any complexity.
Update:
Here is a sample adapter. I didn't compile the code. So there might be some errors.
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class TestAdapter extends BaseAdapter
{
ArrayList<String> deviceNames;
ArrayList<Boolean> selected;
Context context;
public TestAdapter(Context context)
{
this.context = context;
deviceNames = new ArrayList<String>();
selected = new ArrayList<Boolean>();
}
public void addDeviceToList(String deviceName, boolean isSelected)
{
deviceNames.add(deviceName);
selected.add(isSelected);
notifyDataSetChanged();
}
public int getCount()
{
return deviceNames.size();
}
public Object getItem(int position)
{
return deviceNames.get(position);
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
TextView tv = new TextView(context);
tv.setText(deviceNames.get(position));
if(selected.get(position) == true)
{
tv.setBackgroundColor(Color.parseColor("#ff0000"));
}
return tv;
}
}
Now create adapter object and set the adapter to listView. And add single item by calling addDeviceToList() method.
That seems to nasty
but i think you want to modify the views inside listview before loading it
The thing is, that your list won't have children as long as the list is not displayed to the user.so you may not modify the view before showing it to the user
But if you really need to communicate with the views on a such low level you could try to attach a scroll listener to your list:
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
for (int i = 0; i < visibleItemCount; i++) {
View child = getChildAt(i);
Now edit this view
}
}

Android: ListView elements with multiple clickable buttons

I've a ListView where every element in the list contains a TextView and two different Buttons. Something like this:
ListView
--------------------
[Text]
[Button 1][Button 2]
--------------------
[Text]
[Button 1][Button 2]
--------------------
... (and so on) ...
With this code I can create an OnItemClickListener for the whole item:
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> list, View view, int position, long id) {
Log.i(TAG, "onListItemClick: " + position);
}
}
});
However, I don't want the whole item to be clickable, but only the two buttons of each list element.
So my question is, how do I implement a onClickListener for these two buttons with the following parameters:
int button (which button of the element has been clicked)
int position (which is the element in the list on which the button click happened)
Update: I found a solution as described in my answer below. Now I can click/tap the button via the touch screen. However, I can't manually select it with the trackball. It always selects the whole list item and from there goes directly to the next list item ignoring the buttons, even though I set .setFocusable(true) and setClickable(true) for the buttons in getView().
I also added this code to my custom list adapter:
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(int position) {
return false;
}
This causes that no list item is selectable at all any more. But it didn't help in making the nested buttons selectable.
Anyone an idea?
The solution to this is actually easier than I thought. You can simply add in your custom adapter's getView() method a setOnClickListener() for the buttons you're using.
Any data associated with the button has to be added with myButton.setTag() in the getView() and can be accessed in the onClickListener via view.getTag()
I posted a detailed solution on my blog as a tutorial.
This is sort of an appendage #znq's answer...
There are many cases where you want to know the row position for a clicked item AND you want to know which view in the row was tapped. This is going to be a lot more important in tablet UIs.
You can do this with the following custom adapter:
private static class CustomCursorAdapter extends CursorAdapter {
protected ListView mListView;
protected static class RowViewHolder {
public TextView mTitle;
public TextView mText;
}
public CustomCursorAdapter(Activity activity) {
super();
mListView = activity.getListView();
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// do what you need to do
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = View.inflate(context, R.layout.row_layout, null);
RowViewHolder holder = new RowViewHolder();
holder.mTitle = (TextView) view.findViewById(R.id.Title);
holder.mText = (TextView) view.findViewById(R.id.Text);
holder.mTitle.setOnClickListener(mOnTitleClickListener);
holder.mText.setOnClickListener(mOnTextClickListener);
view.setTag(holder);
return view;
}
private OnClickListener mOnTitleClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
final int position = mListView.getPositionForView((View) v.getParent());
Log.v(TAG, "Title clicked, row %d", position);
}
};
private OnClickListener mOnTextClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
final int position = mListView.getPositionForView((View) v.getParent());
Log.v(TAG, "Text clicked, row %d", position);
}
};
}
For future readers:
To select manually the buttons with the trackball use:
myListView.setItemsCanFocus(true);
And to disable the focus on the whole list items:
myListView.setFocusable(false);
myListView.setFocusableInTouchMode(false);
myListView.setClickable(false);
It works fine for me, I can click on buttons with touchscreen and also alows focus an click using keypad
I don't have much experience than above users but I faced this same issue and I Solved this with below Solution
<Button
android:id="#+id/btnRemove"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/btnEdit"
android:layout_weight="1"
android:background="#drawable/btn"
android:text="#string/remove"
android:onClick="btnRemoveClick"
/>
btnRemoveClick Click event
public void btnRemoveClick(View v)
{
final int position = listviewItem.getPositionForView((View) v.getParent());
listItem.remove(position);
ItemAdapter.notifyDataSetChanged();
}
Probably you've found how to do it, but you can call
ListView.setItemsCanFocus(true)
and now your buttons will catch focus
I am not sure about be the best way, but works fine and all code stays in your ArrayAdapter.
package br.com.fontolan.pessoas.arrayadapter;
import java.util.List;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import br.com.fontolan.pessoas.R;
import br.com.fontolan.pessoas.model.Telefone;
public class TelefoneArrayAdapter extends ArrayAdapter<Telefone> {
private TelefoneArrayAdapter telefoneArrayAdapter = null;
private Context context;
private EditText tipoEditText = null;
private EditText telefoneEditText = null;
private ImageView deleteImageView = null;
public TelefoneArrayAdapter(Context context, List<Telefone> values) {
super(context, R.layout.telefone_form, values);
this.telefoneArrayAdapter = this;
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.telefone_form, parent, false);
tipoEditText = (EditText) view.findViewById(R.id.telefone_form_tipo);
telefoneEditText = (EditText) view.findViewById(R.id.telefone_form_telefone);
deleteImageView = (ImageView) view.findViewById(R.id.telefone_form_delete_image);
final int i = position;
final Telefone telefone = this.getItem(position);
tipoEditText.setText(telefone.getTipo());
telefoneEditText.setText(telefone.getTelefone());
TextWatcher tipoTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
telefoneArrayAdapter.getItem(i).setTipo(s.toString());
telefoneArrayAdapter.getItem(i).setIsDirty(true);
}
};
TextWatcher telefoneTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
telefoneArrayAdapter.getItem(i).setTelefone(s.toString());
telefoneArrayAdapter.getItem(i).setIsDirty(true);
}
};
tipoEditText.addTextChangedListener(tipoTextWatcher);
telefoneEditText.addTextChangedListener(telefoneTextWatcher);
deleteImageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
telefoneArrayAdapter.remove(telefone);
}
});
return view;
}
}
I Know it's late but this may help, this is an example how I write custom adapter class for different click actions
public class CustomAdapter extends BaseAdapter {
TextView title;
Button button1,button2;
public long getItemId(int position) {
return position;
}
public int getCount() {
return mAlBasicItemsnav.size(); // size of your list array
}
public Object getItem(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.listnavsub_layout, null, false); // use sublayout which you want to inflate in your each list item
}
title = (TextView) convertView.findViewById(R.id.textViewnav); // see you have to find id by using convertView.findViewById
title.setText(mAlBasicItemsnav.get(position));
button1=(Button) convertView.findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//your click action
// if you have different click action at different positions then
if(position==0)
{
//click action of 1st list item on button click
}
if(position==1)
{
//click action of 2st list item on button click
}
});
// similarly for button 2
button2=(Button) convertView.findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//your click action
});
return convertView;
}
}
Isn't the platform solution for this implementation to use a context menu that shows on a long press?
Is the question author aware of context menus? Stacking up buttons in a listview has performance implications, will clutter your UI and violate the recommended UI design for the platform.
On the flipside; context menus - by nature of not having a passive representation - are not obvious to the end user. Consider documenting the behaviour?
This guide should give you a good start.
http://www.mikeplate.com/2010/01/21/show-a-context-menu-for-long-clicks-in-an-android-listview/

Categories

Resources