Ответ 1
EDIT: между другими вариантами есть хорошая библиотека, которая могла бы решить вашу проблему: https://github.com/daimajia/AndroidSwipeLayout
У меня есть пользовательский ListView, показывающий список слов, выбираемых из базы данных. Когда я прокручиваю этот элемент списка, я хочу показать кнопку Удалить, как показано ниже. И когда я нажимаю эту кнопку, она удаляется из базы данных и обновляет список.
т
Я уже смотрю в этом примере здесь. Но он все еще не работает.
EDIT: между другими вариантами есть хорошая библиотека, которая могла бы решить вашу проблему: https://github.com/daimajia/AndroidSwipeLayout
Я много раз искал google и нашел подходящий проект - swipmenulistview https://github.com/baoyongzhang/SwipeMenuListView на github.
У меня была та же проблема, что и найти хорошую библиотеку. В конце концов, я создал библиотеку, которая может это сделать: SwipeRevealLayout
В файле gradle:
dependencies {
compile 'com.chauthai.swipereveallayout:swipe-reveal-layout:1.4.0'
}
В вашем XML файле:
<com.chauthai.swipereveallayout.SwipeRevealLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mode="same_level"
app:dragEdge="left">
<!-- Your secondary layout here -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent" />
<!-- Your main layout here -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.chauthai.swipereveallayout.SwipeRevealLayout>
Затем в файле адаптера:
public class Adapter extends RecyclerView.Adapter {
// This object helps you save/restore the open/close state of each view
private final ViewBinderHelper viewBinderHelper = new ViewBinderHelper();
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// get your data object first.
YourDataObject dataObject = mDataSet.get(position);
// Save/restore the open/close state.
// You need to provide a String id which uniquely defines the data object.
viewBinderHelper.bind(holder.swipeRevealLayout, dataObject.getId());
// do your regular binding stuff here
}
}
Я создал демонстрацию на моем github, которая включает в себя прокрутку справа налево, появится кнопка удаления, и вы сможете удалить свой элемент из списка ListView и обновить ваш ListView.
Я просто получил его работу с помощью ViewSwitcher в ListItem.
list_item.xml:
<?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="wrap_content"
android:orientation="horizontal" >
<ViewSwitcher
android:id="@+id/list_switcher"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:inAnimation="@android:anim/slide_in_left"
android:outAnimation="@android:anim/slide_out_right"
android:measureAllChildren="false" >
<TextView
android:id="@+id/tv_item_name"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="center_vertical"
android:maxHeight="50dp"
android:paddingLeft="10dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:clickable="false"
android:gravity="center"
>
<Button
android:id="@+id/b_edit_in_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Edit"
android:paddingLeft="20dp"
android:paddingRight="20dp"
/>
<Button
android:id="@+id/b_delete_in_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@android:color/holo_red_dark"
/>
</LinearLayout>
</ViewSwitcher>
В ListAdapter: Внедрите OnclickListeners для кнопки "Редактировать и удалить" в методе getView(). Ловушка здесь заключается в том, чтобы получить позицию ListItem, щелкнув внутри методов onClick. Для этого используются методы setTag() и getTag().
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
final ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null);
viewHolder.viewSwitcher=(ViewSwitcher)convertView.findViewById(R.id.list_switcher);
viewHolder.itemName = (TextView) convertView
.findViewById(R.id.tv_item_name);
viewHolder.deleteitem=(Button)convertView.findViewById(R.id.b_delete_in_list);
viewHolder.deleteItem.setTag(position);
viewHolder.editItem=(Button)convertView.findViewById(R.id.b_edit_in_list);
viewHolder.editItem.setTag(position);
viewHolder.deleteItem.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
fragment.deleteItemList((Integer)v.getTag());
}
});
viewHolder.editItem.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
fragment.editItemList(position);
}
});
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.itemName.setText(itemlist[position]);
return convertView;
}
В фрагменте, Добавьте прослушиватель жестов для обнаружения жестов Fling:
public class MyGestureListener extends SimpleOnGestureListener {
private ListView list;
public MyGestureListener(ListView list) {
this.list = list;
}
// CONDITIONS ARE TYPICALLY VELOCITY OR DISTANCE
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// if (INSERT_CONDITIONS_HERE)
ltor=(e2.getX()-e1.getX()>DELTA_X);
if (showDeleteButton(e1))
{
return true;
}
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// TODO Auto-generated method stub
return super.onScroll(e1, e2, distanceX, distanceY);
}
private boolean showDeleteButton(MotionEvent e1) {
int pos = list.pointToPosition((int) e1.getX(), (int) e1.getY());
return showDeleteButton(pos);
}
private boolean showDeleteButton(int pos) {
View child = list.getChildAt(pos);
if (child != null) {
Button delete = (Button) child
.findViewById(R.id.b_edit_in_list);
ViewSwitcher viewSwitcher = (ViewSwitcher) child
.findViewById(R.id.host_list_switcher);
TextView hostName = (TextView) child
.findViewById(R.id.tv_host_name);
if (delete != null) {
viewSwitcher.setInAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_in_left));
viewSwitcher.setOutAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_out_right));
}
viewSwitcher.showNext();
// frameLayout.setVisibility(View.VISIBLE);
}
return true;
}
return false;
}
}
В методе onCreateView фрагмента
GestureDetector gestureDetector = new GestureDetector(getActivity(),
new MyGestureListener(hostList));
hostList.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if (gestureDetector.onTouchEvent(event)) {
return true;
} else {
return false;
}
}
});
Это сработало для меня. Должен уточнить его больше.
видеть, что ссылка была очень приятной и простой. его рабочий штраф... u не хочу, чтобы какая-либо библиотека работала нормально. нажмите здесь
OnTouchListener gestureListener = new View.OnTouchListener() {
private int padding = 0;
private int initialx = 0;
private int currentx = 0;
private ViewHolder viewHolder;
public boolean onTouch(View v, MotionEvent event) {
if ( event.getAction() == MotionEvent.ACTION_DOWN) {
padding = 0;
initialx = (int) event.getX();
currentx = (int) event.getX();
viewHolder = ((ViewHolder) v.getTag());
}
if ( event.getAction() == MotionEvent.ACTION_MOVE) {
currentx = (int) event.getX();
padding = currentx - initialx;
}
if ( event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL) {
padding = 0;
initialx = 0;
currentx = 0;
}
if(viewHolder != null) {
if(padding == 0) {
v.setBackgroundColor(0xFF000000 );
if(viewHolder.running)
v.setBackgroundColor(0xFF058805);
}
if(padding > 75) {
viewHolder.running = true;
v.setBackgroundColor(0xFF00FF00 );
viewHolder.icon.setImageResource(R.drawable.clock_running);
}
if(padding < -75) {
viewHolder.running = false;
v.setBackgroundColor(0xFFFF0000 );
}
v.setPadding(padding, 0,0, 0);
}
return true;
}
};
Доступно приложение, которое демонстрирует список, который сочетает в себе как перетаскивание, так и перетаскивание для переупорядочения элементов. Код основан на коде Chet Haase для прокрутки для удаления и кода Даниэля Ольшанского для перетаскивания по порядку.
Четный код немедленно удаляет элемент. Я улучшил это, сделав его более похожим на Gmail, где swiping показывает нижний вид, который указывает, что элемент удален, но предоставляет кнопку отмены, где пользователь имеет возможность отменить удаление. Четный код также имеет ошибку. Если в списке есть меньше элементов, чем высота списка, и вы удаляете последний элемент, последний элемент не удаляется. Это было исправлено в моем коде.
Код Daniel требует длительного нажатия на элемент. Многие пользователи считают это неинтуитивным, поскольку он имеет тенденцию быть скрытой функцией. Вместо этого я изменил код, чтобы разрешить кнопку "Переместить". Вы просто нажимаете на кнопку и перетаскиваете элемент. Это больше соответствует тому, как работает приложение Google News при изменении порядка новостей.
Исходный код вместе с демонстрационным приложением доступен по адресу: https://github.com/JohannBlake/ListViewOrderAndSwipe
Чет и Дэниел - оба из Google.
Четное видео по удалению элементов можно просмотреть по адресу: https://www.youtube.com/watch?v=YCHNAi9kJI4
Видео Даниэля о переупорядочивании можно просмотреть по адресу: https://www.youtube.com/watch?v=_BZIvjMgH-Q
Значительная часть работы пошла на склеивание всего этого, чтобы обеспечить бесшумный опыт пользовательского интерфейса, поэтому я буду благодарен за "Like" или "Up Vote". Пожалуйста, также запустите проект в Github.
Определите ViewPager в вашем макете .xml:
<android.support.v4.view.ViewPager
android:id="@+id/example_pager"
android:layout_width="fill_parent"
android:layout_height="@dimen/abc_action_bar_default_height" />
И затем в вашей активности/фрагменте установите пользовательский адаптер пейджера:
В действии:
protected void onCreate(Bundle savedInstanceState) {
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
ViewPager pager = (ViewPager) findViewById(R.id.example_pager);
pager.setAdapter(adapter);
// pager.setOnPageChangeListener(this); // You can set a page listener here
pager.setCurrentItem(0);
}
В фрагменте:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
if (view != null) {
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
ViewPager pager = (ViewPager) view.findViewById(R.id.example_pager);
pager.setAdapter(adapter);
// pager.setOnPageChangeListener(this); // You can set a page listener here
pager.setCurrentItem(0);
}
return view;
}
Создайте наш пользовательский класс пейджера:
// setup your PagerAdapter which extends FragmentPagerAdapter
class PagerAdapter extends FragmentPagerAdapter {
public static final int NUM_PAGES = 2;
private CustomFragment[] mFragments = new CustomFragment[NUM_PAGES];
public PagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@ Override
public int getCount() {
return NUM_PAGES;
}
@ Override
public Fragment getItem(int position) {
if (mFragments[position] == null) {
// this calls the newInstance from when you setup the ListFragment
mFragments[position] = new CustomFragment();
}
return mFragments[position];
}
}
Это потеря времени, чтобы реализовать с нуля эту функциональность. Я реализовал библиотеку, рекомендованную SalutonMondo, и я очень доволен. Он очень прост в использовании и очень быстрый. Я улучшил исходную библиотеку, и я добавил новый клик для элемента click. Также я добавил шрифт awesome library (http://fortawesome.github.io/Font-Awesome/), и теперь вы можете просто добавить новый заголовок элемента и указать имя значка от шрифта awesome.
Здесь - ссылка github
Я потратил тонны сторонних библиотек, чтобы попытаться достичь этого. Но ни один из них не демонстрирует гладкость и удобство использования, которые я хотел. Тогда я решил написать это сам. И результат был, ну, мне понравилось. Я поделюсь кодом здесь. Возможно, я напишу его как библиотеку, которая может быть встроена в любой вид ресайклера в будущем. Но теперь вот код.
Примечание. У меня есть просмотр recycler и ViewHolder. Некоторые значения жестко закодированы, поэтому измените их в соответствии с вашими требованиями.
row_layout.xml
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:orientation="horizontal">
<Button
android:id="@+id/slide_button_2"
android:text="Button2"
android:layout_width="80dp"
android:layout_height="80dp" />
<Button
android:id="@+id/slide_button_1"
android:text="Button1"
android:layout_width="80dp"
android:layout_height="80dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/chat_row_cell"
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="horizontal"
android:background="@color/white">
<ImageView
android:id="@+id/chat_image"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:layout_gravity="center"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/chat_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/md_grey_800"
android:textSize="18sp"/>
<TextView
android:id="@+id/chat_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/md_grey_600"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
ChatAdaptor.java
Открытый класс ChatAdaptor расширяет RecyclerView.Adapter {
List<MXGroupChatSession> sessions;
Context context;
ChatAdaptorInterface listener;
public interface ChatAdaptorInterface{
void cellClicked(MXGroupChatSession session);
void utilityButton1Clicked(MXGroupChatSession session);
void utilityButton2Clicked(MXGroupChatSession session);
}
public ChatAdaptor(List<MXGroupChatSession> sessions, ChatAdaptorInterface listener, Context context){
this.sessions=sessions;
this.context=context;
this.listener=listener;
}
@Override
public ChatViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=(View)LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_row,null);
ChatViewHolder chatViewHolder=new ChatViewHolder(view);
return chatViewHolder;
}
@Override
public void onBindViewHolder(ChatViewHolder holder, final int position) {
MXGroupChatSession session=this.sessions.get(position);
holder.selectedSession=session;
holder.titleView.setText(session.getTopic());
holder.subtitleView.setText(session.getLastFeedContent());
Picasso.with(context).load(new File(session.getCoverImagePath())).transform(new CircleTransformPicasso()).into(holder.imageView);
}
@Override
public int getItemCount() {
return sessions.size();
}
public class ChatViewHolder extends RecyclerView.ViewHolder{
ImageView imageView;
TextView titleView;
TextView subtitleView;
ViewGroup cell;
ViewGroup cellContainer;
Button button1;
Button button2;
MXGroupChatSession selectedSession;
private GestureDetectorCompat gestureDetector;
float totalx;
float buttonTotalWidth;
Boolean open=false;
Boolean isScrolling=false;
public ChatViewHolder(View itemView) {
super(itemView);
cell=(ViewGroup) itemView.findViewById(R.id.chat_row_cell);
cellContainer=(ViewGroup) itemView.findViewById(R.id.chat_row_container);
button1=(Button) itemView.findViewById(R.id.slide_button_1);
button2=(Button) itemView.findViewById(R.id.slide_button_2);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.utilityButton1Clicked(selectedSession);
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.utilityButton2Clicked(selectedSession);
}
});
ViewTreeObserver vto = cellContainer.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
buttonTotalWidth = button1.getWidth()+button2.getWidth();
}
});
this.titleView=(TextView)itemView.findViewById(R.id.chat_title);
subtitleView=(TextView)itemView.findViewById(R.id.chat_subtitle);
imageView=(ImageView)itemView.findViewById(R.id.chat_image);
gestureDetector=new GestureDetectorCompat(context,new ChatRowGesture());
cell.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(gestureDetector.onTouchEvent(event)){
return true;
}
if(event.getAction() == MotionEvent.ACTION_UP) {
if(isScrolling ) {
isScrolling = false;
handleScrollFinished();
};
}
else if(event.getAction() == MotionEvent.ACTION_CANCEL){
if(isScrolling ) {
isScrolling = false;
handleScrollFinished();
};
}
return false;
}
});
}
public class ChatRowGesture extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (!open){
listener.cellClicked(selectedSession);
}
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
isScrolling=true;
totalx=totalx+distanceX;
freescroll(totalx);
return true;
}
}
void handleScrollFinished(){
if (open){
if (totalx>2*buttonTotalWidth/3){
slideLeft();
totalx=buttonTotalWidth;
}else{
slideRight();
totalx=0;
}
}else{
if (totalx>buttonTotalWidth/3){
slideLeft();
totalx=buttonTotalWidth;
}else{
slideRight();
totalx=0;
}
}
}
void slideRight(){
TransitionManager.beginDelayedTransition(cellContainer);
ViewGroup.MarginLayoutParams params;
params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
params.setMargins(0,0,0,0);
cell.setLayoutParams(params);
open=false;
}
void slideLeft(){
TransitionManager.beginDelayedTransition(cellContainer);
ViewGroup.MarginLayoutParams params;
params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
params.setMargins(((int)buttonTotalWidth*-1),0,(int)buttonTotalWidth,0);
cell.setLayoutParams(params);
open=true;
}
void freescroll(float x){
if (x<buttonTotalWidth && x>0){
int xint=(int)x;
ViewGroup.MarginLayoutParams params;
params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
params.setMargins(params.leftMargin,0,xint,0);
cell.setLayoutParams(params);
}
}
}
Надеюсь, это поможет кому-то!
Вы можете использовать этот код
holder.img_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
holder.swipeRevealLayout.close(true);
list.remove(position);
notifyDataSetChanged();
}});