Skip to content

Commit

Permalink
Basic drag-and-drop and swipe-to-dismiss with ItemTouchHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
iPaulPro committed Jun 14, 2015
0 parents commit d8d85c3
Show file tree
Hide file tree
Showing 34 changed files with 762 additions and 0 deletions.
56 changes: 56 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# ndk generated files
**/obj/

# built application files
*.apk
*.ap_

# lint files
lint.xml

# files for the dex VM
*.dex

# Java class files
*.class

# generated files
bin/
gen/
classes/
gen-external-apklibs/

# maven output folder
target

# Local configuration file (sdk path, etc)
local.properties

# Eclipse project files
.classpath
.project
.metadata
.settings

# IntelliJ files
.idea
*.iml

# OSX files
.DS_Store

# Windows files
Thumbs.db

# vi swap files
*.swp

# backup files
*.bak

com_crashlytics_export_strings.xml

# Gradle files
.gradle
/build
/captures
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
20 changes: 20 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 22
buildToolsVersion "22.0.1"

defaultConfig {
applicationId "co.paulburke.android.itemtouchhelperdemo"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:recyclerview-v7:22.2.0'
}
17 changes: 17 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/paulburke/Sites/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package co.paulburke.android.itemtouchhelperdemo;

import android.app.Application;
import android.test.ApplicationTestCase;

/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}
21 changes: 21 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="co.paulburke.android.itemtouchhelperdemo" >

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package co.paulburke.android.itemtouchhelperdemo;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;

/**
* @author Paul Burke (ipaulpro)
*/
public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package co.paulburke.android.itemtouchhelperdemo;

import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import co.paulburke.android.itemtouchhelperdemo.helper.ItemTouchHelperAdapter;
import co.paulburke.android.itemtouchhelperdemo.helper.ItemTouchHelperViewHolder;

/**
* @author Paul Burke (ipaulpro)
*/
public class RecyclerListAdapter extends RecyclerView.Adapter<RecyclerListAdapter.ItemViewHolder>
implements ItemTouchHelperAdapter {

private static final String[] STRINGS = new String[]{
"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"
};

private final List<String> mItems = new ArrayList<>();

public RecyclerListAdapter() {
mItems.addAll(Arrays.asList(STRINGS));
}

@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main, parent, false);
ItemViewHolder itemViewHolder = new ItemViewHolder(view);
return itemViewHolder;
}

@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
holder.textView.setText(mItems.get(position));
}

@Override
public void onItemDismiss(int position) {
mItems.remove(position);
notifyItemRemoved(position);
}

@Override
public void onItemMove(int fromPosition, int toPosition) {
String prev = mItems.remove(fromPosition);
mItems.add(toPosition > fromPosition ? toPosition - 1 : toPosition, prev);
notifyItemMoved(fromPosition, toPosition);
}

@Override
public int getItemCount() {
return mItems.size();
}

public static class ItemViewHolder extends RecyclerView.ViewHolder implements
ItemTouchHelperViewHolder {

public final TextView textView;

public ItemViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView;
}

@Override
public void onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY);
}

@Override
public void onItemClear() {
itemView.setBackgroundColor(0);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package co.paulburke.android.itemtouchhelperdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import co.paulburke.android.itemtouchhelperdemo.helper.SimpleItemTouchHelperCallback;

/**
* @author Paul Burke (ipaulpro)
*/
public class RecyclerListFragment extends Fragment {

private ItemTouchHelper mItemTouchHelper;

public RecyclerListFragment() {
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

RecyclerListAdapter adapter = new RecyclerListAdapter();

RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(recyclerView);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package co.paulburke.android.itemtouchhelperdemo.helper;

import android.support.v7.widget.RecyclerView;

/**
* Interface to notify a {@link RecyclerView.Adapter} of moving and dismissal event from a {@link
* android.support.v7.widget.helper.ItemTouchHelper.Callback}.
*
* @author Paul Burke (ipaulpro)
*/
public interface ItemTouchHelperAdapter {

/**
* Called when an item has been dragged far enough to trigger a move. This is called every time
* an item is shifted, and not at the end of a "drop" event.
*
* @param fromPosition The start position of the moved item.
* @param toPosition Then end position of the moved item.
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
* @see RecyclerView.ViewHolder#getAdapterPosition()
*/
void onItemMove(int fromPosition, int toPosition);


/**
* Called when an item has been dismissed by a swipe.
*
* @param position The position of the item dismissed.
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
* @see RecyclerView.ViewHolder#getAdapterPosition()
*/
void onItemDismiss(int position);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package co.paulburke.android.itemtouchhelperdemo.helper;

import android.support.v7.widget.helper.ItemTouchHelper;

/**
* Interface to notify an item ViewHolder of relevant callbacks from {@link
* android.support.v7.widget.helper.ItemTouchHelper.Callback}.
*
* @author Paul Burke (ipaulpro)
*/
public interface ItemTouchHelperViewHolder {

/**
* Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
* Implementations should update the item view to indicate it's active state.
*/
void onItemSelected();


/**
* Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
* state should be cleared.
*/
void onItemClear();
}
Loading

1 comment on commit d8d85c3

@dwilches
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small mistake:
mItems.add(toPosition > fromPosition ? toPosition - 1 : toPosition, prev);
Should be:
mItems.add(toPosition > fromPosition + 1 ? toPosition - 1 : toPosition, prev);

The reason is: consider what happens when you move an element exactly one position down.
If you have this list: A B C
And you move A to get this: B A C

Then fromPosition will be 0, and toPosition will be 1. Now, your original line's if will execute the 'then' that makes that statement into: mItems.add(toPosition - 1, prev);

Which means, add A at the position 0 ... again.

Please sign in to comment.