Required:
1.I want to Make a Sectioned Header Listview in Android.
2.That should be Swipe to delete a item, with Undo option like Gmail App.but Gmail App not contains Section Header.
3.My Application should be have Section Header.
i tried below mentioned link for Swipe to delete along with Undo button. its worked perfectly.
Problem:
EDIT :1(ListView)
1.i found the code for Swipe to Delete a item with Undo in Listview Link-Swipe to delete a listview item and Section Header Using Listview link-Section header in listview.
2.both are having two different Base Adapters Iam getting some Error,Please help me to merge that adapters or suggest me any new way to add section Header in Swipe to Delete a Listview items.
CODE: Class for Swipe to Delete and Section Header in Listview
ListViewActivity.class
package com.data.swipetodeletesimplelistview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import static android.widget.Toast.LENGTH_SHORT;
public class ListViewActivity extends AppCompatActivity {
private static final int TIME_TO_AUTOMATICALLY_DISMISS_ITEM = 3000;
/*For Section header*/
ListView mListView;
ArrayList<String> mArrayList = new ArrayList<String>();
SectionedAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
init((ListView) findViewById(R.id.list_view));
/*For Section Header Starts Here*/
mListView = (ListView) findViewById(R.id.list_view);
adapter = new SectionedAdapter() {
@Override
protected View getHeaderView(String caption, int index, View convertView, ViewGroup parent) {
convertView = getLayoutInflater().inflate(R.layout.section_header, null);
TextView header = (TextView) convertView.findViewById(R.id.header);
header.setText(caption);
return convertView;
}
};
for (int i = 0; i < 5; i++)
{
mArrayList.add("Item " + i);
MyAdapter myAdapter = new MyAdapter();
adapter.addSection("Header " + i, myAdapter);
}
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
Toast.makeText(getApplicationContext(), arg0.getAdapter().getItem(position).toString(), Toast.LENGTH_LONG).show();
}
});
mListView.setAdapter(adapter);
/*For Section Header Ends Here*/
}
/*FOr Swipe to Delete a item Starts Here*/
private void init(ListView listView)
{
final MyBaseAdapter adapter = new MyBaseAdapter();
listView.setAdapter(adapter);
final SwipeToDismissTouchListener<ListViewAdapter> touchListener =
new SwipeToDismissTouchListener<>(
new ListViewAdapter(listView),
new SwipeToDismissTouchListener.DismissCallbacks<ListViewAdapter>() {
@Override
public boolean canDismiss(int position) {
return true;
}
@Override
public void onPendingDismiss(ListViewAdapter recyclerView, int position) {
}
@Override
public void onDismiss(ListViewAdapter view, int position) {
adapter.remove(position);
}
});
touchListener.setDismissDelay(TIME_TO_AUTOMATICALLY_DISMISS_ITEM);
listView.setOnTouchListener(touchListener);
// Setting this scroll listener is required to ensure that during ListView scrolling,
// we don't look for swipes.
listView.setOnScrollListener((AbsListView.OnScrollListener) touchListener.makeScrollListener());
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (touchListener.existPendingDismisses()) {
touchListener.undoPendingDismiss();
} else {
Toast.makeText(ListViewActivity.this, "Position " + position, LENGTH_SHORT).show();
}
}
});
}
/*FOr Swipe to Delete*/
static class MyBaseAdapter extends BaseAdapter
{
private static final int SIZE = 100;
private final List<String> mDataSet = new ArrayList<>();
MyBaseAdapter() {
for (int i = 0; i < SIZE; i++)
mDataSet.add(i, "This is row number " + i);
}
@Override
public int getCount() {
return mDataSet.size();
}
@Override
public String getItem(int position) {
return mDataSet.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
public void remove(int position) {
mDataSet.remove(position);
notifyDataSetChanged();
}
static class ViewHolder {
TextView dataTextView;
ViewHolder(View view) {
dataTextView = (TextView) view.findViewById(R.id.txt_data);
view.setTag(this);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = convertView == null
? new ViewHolder(convertView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false)) : (ViewHolder) convertView.getTag();
viewHolder.dataTextView.setText(mDataSet.get(position));
return convertView;
}
}
/*FOr Swipe to Delete a item Ends Here*/
/*For adding Section header*/
class MyAdapter extends BaseAdapter
{
public int getCount()
{
return mArrayList.size();
}
public Object getItem(int position)
{
return mArrayList.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
convertView = (TextView) getLayoutInflater().inflate(R.layout.section_item, null);
TextView item = (TextView) convertView.findViewById(R.id.item);
item.setText(mArrayList.get(position));
return convertView;
}
}
}
You basically wanted to have two type of rows in the list.
Create a recyclerView or listView by inflating two different type of layout. Check How to create RecyclerView with multiple view type?
Edit: Swipe to delete, Undo and Sectioned list Adapter issue
As mentioned by @MadScientist, there should be only one Adapter for the list or recyclerView. Follow following steps to achieve your requirements:
See below sample code of RecyclerAdapter for sectioned header and undo functionality:
public class SectionedRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private List<SectionedItem> sectionedItemList;
private List<SectionedItem> itemsPendingRemoval;
private Context context;
private static final int PENDING_REMOVAL_TIMEOUT = 3000;
private Handler handler = new Handler();
private HashMap<SectionedItem, Runnable> pendingRunnables = new HashMap<>();
public SectionedRecyclerAdapter(List<SectionedItem> itemList, Context context) {
this.sectionedItemList = itemList;
this.context = context;
itemsPendingRemoval = new ArrayList<>();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new SectionedItemViewHolder(view);
} else if (viewType == TYPE_HEADER) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_header, parent, false);
return new SectionedHeaderViewHolder(view);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
if (viewHolder instanceof SectionedItemViewHolder) {
final SectionedItem data = sectionedItemList.get(position);
if (itemsPendingRemoval.contains(data)) {
((SectionedItemViewHolder) viewHolder).itemLayout.setVisibility(View.GONE);
((SectionedItemViewHolder) viewHolder).undoLayout.setVisibility(View.VISIBLE);
((SectionedItemViewHolder) viewHolder).undobutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
undoOpt(data);
}
});
} else {
((SectionedItemViewHolder) viewHolder).itemLayout.setVisibility(View.VISIBLE);
((SectionedItemViewHolder) viewHolder).undoLayout.setVisibility(View.GONE);
((SectionedItemViewHolder) viewHolder).itemName.setText(sectionedItemList.get(position).itemName);
}
}
if (viewHolder instanceof SectionedHeaderViewHolder) {
((SectionedHeaderViewHolder) viewHolder).headerTitle.setText(sectionedItemList.get(position).itemName);
}
}
@Override
public int getItemCount() {
return sectionedItemList.size();
}
@Override
public int getItemViewType(int position) {
if (isPositionHeader(position)) {
return TYPE_HEADER;
}
return TYPE_ITEM;
}
private boolean isPositionHeader(int position) {
return sectionedItemList.get(position).isHeader;
}
private void undoOpt(SectionedItem customer) {
Runnable pendingRemovalRunnable = pendingRunnables.get(customer);
pendingRunnables.remove(customer);
if (pendingRemovalRunnable != null)
handler.removeCallbacks(pendingRemovalRunnable);
itemsPendingRemoval.remove(customer);
// this will rebind the row in "normal" state
notifyItemChanged(sectionedItemList.indexOf(customer));
}
public void pendingRemoval(int position) {
final SectionedItem data = sectionedItemList.get(position);
if (!itemsPendingRemoval.contains(data) && !data.isHeader) {
itemsPendingRemoval.add(data);
// this will redraw row in "undo" state
notifyItemChanged(position);
// let's create, store and post a runnable to remove the data
Runnable pendingRemovalRunnable = new Runnable() {
@Override
public void run() {
remove(sectionedItemList.indexOf(data));
}
};
handler.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT);
pendingRunnables.put(data, pendingRemovalRunnable);
}
}
public void remove(int position) {
SectionedItem data = sectionedItemList.get(position);
if (itemsPendingRemoval.contains(data)) {
itemsPendingRemoval.remove(data);
}
if (sectionedItemList.contains(data)) {
sectionedItemList.remove(position);
notifyItemRemoved(position);
}
}
private void removeItemPermanently(int position) {
sectionedItemList.get(position).isSoftDeleted = false;
sectionedItemList.remove(position);
notifyItemRemoved(position);
}
public boolean isPendingRemoval(int position) {
SectionedItem data = sectionedItemList.get(position);
return (itemsPendingRemoval.contains(data) || data.isHeader);
}
}
I have used link as reference to implement undo bar
Add SwipeUtil.java class as it is from the above link and update your Activity class like below: Add this function to your Activity class
private void setSwipeForRecyclerView() {
SwipeUtils swipeHelper = new SwipeUtils(0, ItemTouchHelper.LEFT, ActivityB.this) {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int swipedPosition = viewHolder.getAdapterPosition();
SectionedRecyclerAdapter adapter = (SectionedRecyclerAdapter) sectionedList.getAdapter();
adapter.pendingRemoval(swipedPosition);
}
@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int position = viewHolder.getAdapterPosition();
SectionedRecyclerAdapter adapter = (SectionedRecyclerAdapter) sectionedList.getAdapter();
if (adapter.isPendingRemoval(position)) {
return 0;
}
return super.getSwipeDirs(recyclerView, viewHolder);
}
};
ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(swipeHelper);
mItemTouchHelper.attachToRecyclerView(sectionedList);
//set swipe label
swipeHelper.setLeftSwipeLable("Deleted");
//set swipe background-Color
//swipeHelper.setLeftcolorCode(ContextCompat.getColor((), R.color.swipebg));
}
And use below code to create the list:
sectionedList = (RecyclerView)findViewById(R.id.sectioned_list);
RecyclerView.LayoutManager layoutManager= new LinearLayoutManager(this);
sectionedList.setLayoutManager(layoutManager);
final SectionedRecyclerAdapter sectionedRecyclerAdapter = new SectionedRecyclerAdapter(itemList, this);
sectionedList.setAdapter(sectionedRecyclerAdapter);
setSwipeForRecyclerView();
Edit2: ViewHolders
public class SectionedItemViewHolder extends RecyclerView.ViewHolder{
public TextView itemName;
public TextView undobutton;
public View itemLayout;
public View undoLayout;
public SectionedItemViewHolder(View itemView) {
super(itemView);
itemName = (TextView)itemView.findViewById(R.id.item_title);
undobutton = (TextView) itemView.findViewById(R.id.txt_undo);
itemLayout = itemView.findViewById(R.id.item_layout);
undoLayout = itemView.findViewById(R.id.undo_layout);
}
}
public class SectionedHeaderViewHolder extends RecyclerView.ViewHolder{
public TextView headerTitle;
public SectionedHeaderViewHolder(View itemView) {
super(itemView);
headerTitle = (TextView)itemView.findViewById(R.id.header_title);
}
}