diff --git a/README.md b/README.md index 6e9bc3f..2897f03 100644 --- a/README.md +++ b/README.md @@ -359,7 +359,7 @@ directory of your project you can do something like this: ```sh mkdir libraries cd libraries -git submodule add https://github.com/JayH5/drag-sort-listview.git +git submodule add https://github.com/loeschg/drag-sort-listview.git echo "include ':libraries:drag-sort-listview:library'" >> ../settings.gradle ``` Then add the following dependency to your build.gradle project: diff --git a/build.gradle b/build.gradle index 2d3bec7..01f561d 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,6 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.7.+' + classpath 'com.android.tools.build:gradle:0.12.2' } } diff --git a/demo/.settings/org.eclipse.jdt.core.prefs b/demo/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..b080d2d --- /dev/null +++ b/demo/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/demo/AndroidManifest.xml b/demo/AndroidManifest.xml index 14b68e1..6d2c171 100644 --- a/demo/AndroidManifest.xml +++ b/demo/AndroidManifest.xml @@ -8,8 +8,11 @@ android:targetSdkVersion="19" /> + + + diff --git a/demo/build.gradle b/demo/build.gradle index 06154ae..22940c0 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -7,7 +7,7 @@ dependencies { android { compileSdkVersion 19 - buildToolsVersion "19.0.0" + buildToolsVersion "19.1.0" sourceSets { main { diff --git a/demo/project.properties b/demo/project.properties index 3770f74..a6cf15d 100644 --- a/demo/project.properties +++ b/demo/project.properties @@ -12,4 +12,4 @@ # Project target. target=android-19 -android.library.reference.1=../library/ +android.library.reference.1=../library diff --git a/demo/res/layout/checkable_main.xml b/demo/res/layout/checkable_main.xml index 6419b36..c6045c6 100644 --- a/demo/res/layout/checkable_main.xml +++ b/demo/res/layout/checkable_main.xml @@ -1,6 +1,6 @@ + + + + diff --git a/demo/res/layout/section_div.xml b/demo/res/layout/section_div.xml index c48f575..0cbd708 100644 --- a/demo/res/layout/section_div.xml +++ b/demo/res/layout/section_div.xml @@ -1,5 +1,6 @@ + + + + diff --git a/demo/res/layout/warp_main.xml b/demo/res/layout/warp_main.xml index 3631bd7..a2dfc12 100644 --- a/demo/res/layout/warp_main.xml +++ b/demo/res/layout/warp_main.xml @@ -1,7 +1,7 @@ + + + + diff --git a/demo/res/values/preferences_strings.xml b/demo/res/values/preferences_strings.xml new file mode 100644 index 0000000..ee33381 --- /dev/null +++ b/demo/res/values/preferences_strings.xml @@ -0,0 +1,20 @@ + + + + Bob + Fred + George + + + Bob Menwaldo + Fred Frankfurd + George Stephanopolis + + + George + Fred + Bob + + Name Order Preference + Test + diff --git a/demo/res/values/strings.xml b/demo/res/values/strings.xml index 533fc7c..a1a7134 100644 --- a/demo/res/values/strings.xml +++ b/demo/res/values/strings.xml @@ -88,7 +88,7 @@ Mark Turner - Largo, Art of the Trio 1-4, Highway Rider, Songs + Largo, Art of the Trio 1–4, Highway Rider, Songs Elastic, Momentum, Mood Swing, Back East Light as a Feather, Akoustic Band, My Spanish Heart Deep Song, Heartcore, Our Secret World, Reflections, The Remedy: Live at the Village Vanguard, The Enemies of Energy, The Next Step @@ -301,4 +301,7 @@ Zambia Zimbabwe + Settings + Drag Item + Remove Item diff --git a/demo/res/xml/pref_headers.xml b/demo/res/xml/pref_headers.xml new file mode 100644 index 0000000..a32bebd --- /dev/null +++ b/demo/res/xml/pref_headers.xml @@ -0,0 +1,9 @@ + + + + +
+ diff --git a/demo/res/xml/pref_name.xml b/demo/res/xml/pref_name.xml new file mode 100644 index 0000000..de008f5 --- /dev/null +++ b/demo/res/xml/pref_name.xml @@ -0,0 +1,14 @@ + + + diff --git a/demo/src/com/mobeta/android/demodslv/DSLVFragment.java b/demo/src/com/mobeta/android/demodslv/DSLVFragment.java index c7004ff..8263865 100644 --- a/demo/src/com/mobeta/android/demodslv/DSLVFragment.java +++ b/demo/src/com/mobeta/android/demodslv/DSLVFragment.java @@ -94,6 +94,7 @@ protected void setListAdapter() { String[] array = getResources().getStringArray(R.array.jazz_artist_names); List list = new ArrayList(Arrays.asList(array)); + mAdapter = new ArrayAdapter(getActivity(), getItemLayout(), R.id.text, list); setListAdapter(mAdapter); } @@ -129,7 +130,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, return mDslv; } - @Override + @SuppressWarnings("unchecked") + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -145,6 +147,7 @@ public void onActivityCreated(Bundle savedInstanceState) { headers = args.getInt("headers", 0); footers = args.getInt("footers", 0); } + for (int i = 0; i < headers; i++) { addHeader(getActivity(), mDslv); @@ -154,6 +157,7 @@ public void onActivityCreated(Bundle savedInstanceState) { } setListAdapter(); + mAdapter=(ArrayAdapter) getListAdapter(); } diff --git a/demo/src/com/mobeta/android/demodslv/DSLVFragmentClicks.java b/demo/src/com/mobeta/android/demodslv/DSLVFragmentClicks.java index 8b80ceb..0373d81 100644 --- a/demo/src/com/mobeta/android/demodslv/DSLVFragmentClicks.java +++ b/demo/src/com/mobeta/android/demodslv/DSLVFragmentClicks.java @@ -1,5 +1,7 @@ package com.mobeta.android.demodslv; +import java.util.Locale; +import android.annotation.SuppressLint; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; @@ -19,25 +21,25 @@ public static DSLVFragmentClicks newInstance(int headers, int footers) { return f; } - @Override + @Override public void onActivityCreated(Bundle savedState) { super.onActivityCreated(savedState); ListView lv = getListView(); lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override + @Override public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) { - String message = String.format("Clicked item %d", arg2); + String message = String.format(Locale.getDefault(),"Clicked item %d", arg2); Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show(); } }); lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - @Override + @Override public boolean onItemLongClick(AdapterView arg0, View arg1, int arg2, long arg3) { - String message = String.format("Long-clicked item %d", arg2); + String message = String.format(Locale.getDefault(),"Long-clicked item %d", arg2); Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show(); return true; } diff --git a/demo/src/com/mobeta/android/demodslv/Launcher.java b/demo/src/com/mobeta/android/demodslv/Launcher.java index fe0ef0d..5624177 100644 --- a/demo/src/com/mobeta/android/demodslv/Launcher.java +++ b/demo/src/com/mobeta/android/demodslv/Launcher.java @@ -6,6 +6,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -15,66 +17,89 @@ import java.util.ArrayList; import java.util.Arrays; - public class Launcher extends ListActivity { - private ArrayList mActivities = null; - - private String[] mActTitles; - private String[] mActDescs; - - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.launcher); - - try { - PackageInfo pi = getPackageManager().getPackageInfo( - "com.mobeta.android.demodslv", PackageManager.GET_ACTIVITIES); - - mActivities = new ArrayList(Arrays.asList(pi.activities)); - String ourName = getClass().getName(); - for (int i = 0; i < mActivities.size(); ++i) { - if (ourName.equals(mActivities.get(i).name)) { - mActivities.remove(i); - break; - } - } - } catch (PackageManager.NameNotFoundException e) { - // Do nothing. Adapter will be empty. - } - - mActTitles = getResources().getStringArray(R.array.activity_titles); - mActDescs = getResources().getStringArray(R.array.activity_descs); - - setListAdapter(new MyAdapter()); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - Intent intent = new Intent(); - intent.setClassName(this, mActivities.get(position).name); - startActivity(intent); - } - - private class MyAdapter extends ArrayAdapter { - MyAdapter() { - super(Launcher.this, R.layout.launcher_item, R.id.activity_title, mActivities); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = super.getView(position, convertView, parent); - - TextView title = (TextView) v.findViewById(R.id.activity_title); - TextView desc = (TextView) v.findViewById(R.id.activity_desc); - - title.setText(mActTitles[position]); - desc.setText(mActDescs[position]); - return v; - } - - } + private ArrayList mActivities = null; + + private String[] mActTitles; + private String[] mActDescs; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.launcher); + + try { + PackageInfo pi = getPackageManager().getPackageInfo( + "com.mobeta.android.demodslv", PackageManager.GET_ACTIVITIES); + + mActivities = new ArrayList(Arrays.asList(pi.activities)); + String[] excludeList = new String[]{getClass().getName(), + SettingsActivity.class.getName()}; + for (int i = 0; i < mActivities.size(); ++i) { + for (String name: excludeList) { + if (name.equals(mActivities.get(i).name)) { + mActivities.remove(i); + break; + } + } + } + } catch (PackageManager.NameNotFoundException e) { + // Do nothing. Adapter will be empty. + } + + mActTitles = getResources().getStringArray(R.array.activity_titles); + mActDescs = getResources().getStringArray(R.array.activity_descs); + + setListAdapter(new MyAdapter()); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Intent intent = new Intent(); + intent.setClassName(this, mActivities.get(position).name); + startActivity(intent); + } + + private class MyAdapter extends ArrayAdapter { + MyAdapter() { + super(Launcher.this, R.layout.launcher_item, R.id.activity_title, mActivities); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View v = super.getView(position, convertView, parent); + + TextView title = (TextView) v.findViewById(R.id.activity_title); + TextView desc = (TextView) v.findViewById(R.id.activity_desc); + + title.setText(mActTitles[position]); + desc.setText(mActDescs[position]); + return v; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.activity_menu, menu); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + Intent intent; + switch (item.getItemId()) { + case R.id.menu_settings: + intent =new Intent(this,SettingsActivity.class); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } } diff --git a/demo/src/com/mobeta/android/demodslv/SettingsActivity.java b/demo/src/com/mobeta/android/demodslv/SettingsActivity.java new file mode 100644 index 0000000..fc06f47 --- /dev/null +++ b/demo/src/com/mobeta/android/demodslv/SettingsActivity.java @@ -0,0 +1,212 @@ +package com.mobeta.android.demodslv; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Build; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.support.v4.app.NavUtils; +import android.view.MenuItem; + +import java.util.List; + +/** + * A {@link PreferenceActivity} that presents a set of application settings. On + * handset devices, settings are presented as a single list. On tablets, + * settings are split by category, with category headers shown to the left of + * the list of settings. + *

+ * See + * Android Design: Settings for design guidelines and the Settings + * API Guide for more information on developing a Settings UI. + */ +public class SettingsActivity extends PreferenceActivity { + /** + * Determines whether to always show the simplified settings UI, where + * settings are presented in a single list. When false, settings are shown + * as a master/detail two-pane view on tablets. When true, a single pane is + * shown on tablets. + */ + private static final boolean ALWAYS_SIMPLE_PREFS = false; + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + setupSimplePreferencesScreen(); + //This doesn't always work right with HoneyComb, so I removed it. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + { + getActionBar().setDisplayHomeAsUpEnabled(true); + } + } + + /** + * Shows the simplified settings UI if the device configuration if the + * device configuration dictates that a simplified, single-pane UI should be + * shown. + */ + @SuppressWarnings("deprecation") + private void setupSimplePreferencesScreen() { + if (!isSimplePreferences(this)) { + return; + } + + // In the simplified UI, fragments are not used at all and we instead + // use the older PreferenceActivity APIs. + + // Add 'general' preferences. + addPreferencesFromResource(R.xml.pref_name); + + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences to + // their values. When their values change, their summaries are updated + // to reflect the new value, per the Android Design guidelines. + + } + + @Override + public boolean onIsMultiPane() { + return isLargeTablet(this) && !isSimplePreferences(this); + } + + /** + * Helper method to determine if the device has an extra-large screen. For + * example, 10" tablets are extra-large. + */ + private static boolean isLargeTablet(Context context) { + return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + /** + * Determines whether the simplified settings UI should be shown. This is + * true if this is forced via {@link #ALWAYS_SIMPLE_PREFS}, or the device + * doesn't have newer APIs like {@link PreferenceFragment}, or the device + * doesn't have an extra-large screen. In these cases, a single-pane + * "simplified" settings UI should be shown. + */ + private static boolean isSimplePreferences(Context context) { + return ALWAYS_SIMPLE_PREFS + || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB; + } + + /** {@inheritDoc} */ + @Override + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void onBuildHeaders(List

target) { + if (!isSimplePreferences(this)) { + loadHeadersFromResource(R.xml.pref_headers, target); + } + } + + /** + * A preference value change listener that updates the preference's summary + * to reflect its new value. + */ + private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + String stringValue = value.toString(); + + if (preference instanceof ListPreference) { + // For list preferences, look up the correct display value in + // the preference's 'entries' list. + ListPreference listPreference = (ListPreference) preference; + int index = listPreference.findIndexOfValue(stringValue); + + // Set the summary to reflect the new value. + preference + .setSummary(index >= 0 ? listPreference.getEntries()[index] + : null); + } else { + // For all other preferences, set the summary to the value's + // simple string representation. + preference.setSummary(stringValue); + } + return true; + } + }; + + /** + * Binds a preference's summary to its value. More specifically, when the + * preference's value is changed, its summary (line of text below the + * preference title) is updated to reflect the value. The summary is also + * immediately updated upon calling this method. The exact display format is + * dependent on the type of preference. + * + * @see #sBindPreferenceSummaryToValueListener + */ + private static void bindPreferenceSummaryToValue(Preference preference) { + // Set the listener to watch for value changes. + preference + .setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); + + if (preference instanceof CheckBoxPreference) { + // Trigger the listener immediately with the preference's + // current value. + sBindPreferenceSummaryToValueListener.onPreferenceChange( + preference, + PreferenceManager.getDefaultSharedPreferences( + preference.getContext()).getBoolean(preference.getKey(),false)); + } + else if (preference instanceof EditTextPreference) { + + // Trigger the listener immediately with the preference's + // current value. + sBindPreferenceSummaryToValueListener.onPreferenceChange( + preference, + PreferenceManager.getDefaultSharedPreferences( + preference.getContext()).getString(preference.getKey(),"")); + } + else { + throw new IllegalArgumentException("Attempting to bind to unknown type of preference!"); + } + } + + /** + * This fragment shows location update preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class NameSelectionPreferenceFragment extends + PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_name); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + } + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + protected boolean isValidFragment (String fragmentName) + { + if(SettingsActivity.class.getName().equals(fragmentName) || + NameSelectionPreferenceFragment.class.getName().equals(fragmentName)) + return true; + return false; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + NavUtils.navigateUpFromSameTask(this); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 60dc7c2..c27ba22 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-all.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip diff --git a/library/.settings/org.eclipse.jdt.core.prefs b/library/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..b080d2d --- /dev/null +++ b/library/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/library/build.gradle b/library/build.gradle index fe7deed..2b1459a 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,12 +1,24 @@ -apply plugin: 'android-library' +apply plugin: 'com.android.library' dependencies { compile 'com.android.support:support-v4:19.0.+' } +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:0.12.+' + } +} + android { compileSdkVersion 19 - buildToolsVersion "19.0.0" + buildToolsVersion "19.1.0" + project.archivesBaseName = "drag-sort-listview" + project.version = android.defaultConfig.versionName sourceSets { main { diff --git a/library/build.xml b/library/build.xml new file mode 100644 index 0000000..2f6f323 --- /dev/null +++ b/library/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/res/layout/sort_list_array_dialog_preference.xml b/library/res/layout/sort_list_array_dialog_preference.xml new file mode 100644 index 0000000..9fe762e --- /dev/null +++ b/library/res/layout/sort_list_array_dialog_preference.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/library/res/values/dslv_attrs.xml b/library/res/values/dslv_attrs.xml index c7db955..49895ab 100644 --- a/library/res/values/dslv_attrs.xml +++ b/library/res/values/dslv_attrs.xml @@ -1,39 +1,45 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/com/mobeta/android/dslv/DragSortController.java b/library/src/com/mobeta/android/dslv/DragSortController.java index a6fdd3c..c09865e 100644 --- a/library/src/com/mobeta/android/dslv/DragSortController.java +++ b/library/src/com/mobeta/android/dslv/DragSortController.java @@ -382,31 +382,31 @@ public boolean onDown(MotionEvent ev) { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - final int x1 = (int) e1.getX(); - final int y1 = (int) e1.getY(); - final int x2 = (int) e2.getX(); - final int y2 = (int) e2.getY(); - final int deltaX = x2 - mItemX; - final int deltaY = y2 - mItemY; - - if (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) { - if (mHitPos != MISS) { - if (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) { - startDrag(mHitPos, deltaX, deltaY); - } - else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop - && mRemoveEnabled) - { - mIsRemoving = true; - startDrag(mFlingHitPos, deltaX, deltaY); - } - } else if (mFlingHitPos != MISS) { - if (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) { - mIsRemoving = true; - startDrag(mFlingHitPos, deltaX, deltaY); - } else if (Math.abs(y2 - y1) > mTouchSlop) { - mCanDrag = false; // if started to scroll the list then - // don't allow sorting nor fling-removing + if (e1 != null && e2 != null) { + final int x1 = (int) e1.getX(); + final int y1 = (int) e1.getY(); + final int x2 = (int) e2.getX(); + final int y2 = (int) e2.getY(); + final int deltaX = x2 - mItemX; + final int deltaY = y2 - mItemY; + + if (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) { + if (mHitPos != MISS) { + if (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) { + startDrag(mHitPos, deltaX, deltaY); + } else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop + && mRemoveEnabled) { + mIsRemoving = true; + startDrag(mFlingHitPos, deltaX, deltaY); + } + } else { + if (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) { + mIsRemoving = true; + startDrag(mFlingHitPos, deltaX, deltaY); + } else if (Math.abs(y2 - y1) > mTouchSlop) { + mCanDrag = false; // if started to scroll the list then + // don't allow sorting nor fling-removing + } } } } diff --git a/library/src/com/mobeta/android/dslv/DragSortListPreference.java b/library/src/com/mobeta/android/dslv/DragSortListPreference.java new file mode 100644 index 0000000..9b0dcac --- /dev/null +++ b/library/src/com/mobeta/android/dslv/DragSortListPreference.java @@ -0,0 +1,304 @@ +/* + * Sortable Preference ListView. Allows for sorting items in a view, + * and selecting which ones to use. + * + * Example Usage (In a preference file) + * + * + * + * Original Source: https://github.com/kd7uiy/drag-sort-listview + * + * The MIT License (MIT) + * + * Copyright (c) 2013 The Making of a Ham, http://www.kd7uiy.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Code snippets copied from the following sources: + * https://gist.github.com/cardil/4754571 + * + * + */ + +package com.mobeta.android.dslv; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import com.mobeta.android.dslv.DragSortListView.DropListener; + +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.res.TypedArray; +import android.preference.ListPreference; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.ArrayAdapter; + +public class DragSortListPreference extends ListPreference { + private static final String TAG = DragSortListPreference.class.getName(); + + protected DragSortListView mListView; + protected ArrayAdapter mAdapter; + + public static final String DEFAULT_SEPARATOR = "\u0001\u0007\u001D\u0007\u0001"; + private String mSeparator; + + private HashMap entryChecked; + + private int mListPreference; + + private int mAdapterView; + + private int mTextField; + + public DragSortListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + mSeparator = DEFAULT_SEPARATOR; + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, + R.styleable.DragSortListPreference, 0, 0); + + for (int i = 0; i < attrs.getAttributeCount(); i++) { + Log.v(TAG, + attrs.getAttributeName(i) + "=" + + attrs.getAttributeNameResource(i)); + } + + mListPreference = a.getResourceId( + R.styleable.DragSortListPreference_pref_layout, + R.layout.sort_list_array_dialog_preference); + mAdapterView = a.getResourceId( + R.styleable.DragSortListPreference_array_adapter_view, + android.R.layout.simple_list_item_1); + mTextField = a.getResourceId( + R.styleable.DragSortListPreference_text_field, + android.R.id.text1); + + Log.v(TAG, "mListPref=" + mListPreference); + Log.v(TAG, "mAdapterView=" + mAdapterView); + Log.v(TAG, "mTextField=" + mTextField); + + a.recycle(); + } + setDialogLayoutResource(mListPreference); + entryChecked = new HashMap(); + } + + public static CharSequence[] decodeValue(String input) { + return decodeValue(input, DEFAULT_SEPARATOR); + } + + public static CharSequence[] decodeValue(String input, String separator) { + if (input == null) { + return null; + } + if (input.equals("")) { + return new CharSequence[0]; + } + return input.split(separator); + } + + @Override + protected void onBindDialogView(View view) { + super.onBindDialogView(view); + mListView = (DragSortListView) view.findViewById(android.R.id.list); + mAdapter = new ArrayAdapter(mListView.getContext(), + mAdapterView, mTextField); + mListView.setAdapter(mAdapter); + // This will drop the item in the new location + mListView.setDropListener(new DropListener() { + @Override + public void drop(int from, int to) { + CharSequence item = mAdapter.getItem(from); + mAdapter.remove(item); + mAdapter.insert(item, to); + // Updates checked states + mListView.moveCheckState(from, to); + } + }); + // Setting the default values happens in onPrepareDialogBuilder + } + + @Override + protected void onPrepareDialogBuilder(Builder builder) { + Log.v(TAG, "onPrepareDialogBuilder"); + + CharSequence[] entries = getEntries(); + CharSequence[] entryValues = getEntryValues(); + if (entries == null || entryValues == null + || entries.length != entryValues.length) { + throw new IllegalStateException( + "SortableListPreference requires an entries array and an entryValues " + + "array which are both the same length"); + } + + CharSequence[] restoredValues = restoreEntries(); + for (CharSequence value : restoredValues) { + mAdapter.add(entries[getValueIndex(value)]); + } + + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + List values = new ArrayList(); + + CharSequence[] entryValues = getEntryValues(); + if (positiveResult && entryValues != null) { + for (int i = 0; i < entryValues.length; i++) { + String val = (String) mAdapter.getItem(i); + boolean isChecked = mListView.isItemChecked(i); + if (isChecked) { + values.add(entryValues[getValueTitleIndex(val)]); + } + } + + String value = join(values, mSeparator); + setSummary(prepareSummary(values)); + setValueAndEvent(value); + } + } + + private void setValueAndEvent(String value) { + if (callChangeListener(decodeValue(value, mSeparator))) { + setValue(value); + } + } + + @Override + protected Object onGetDefaultValue(TypedArray typedArray, int index) { + return typedArray.getTextArray(index); + } + + @Override + protected void onSetInitialValue(boolean restoreValue, + Object rawDefaultValue) { + String value = null; + CharSequence[] defaultValue; + if (rawDefaultValue == null) { + defaultValue = new CharSequence[0]; + } else { + defaultValue = (CharSequence[]) rawDefaultValue; + } + List joined = Arrays.asList(defaultValue); + String joinedDefaultValue = join(joined, mSeparator); + if (restoreValue) { + value = getPersistedString(joinedDefaultValue); + } else { + value = joinedDefaultValue; + } + + setSummary(prepareSummary(Arrays.asList(decodeValue(value, mSeparator)))); + setValueAndEvent(value); + } + + private String prepareSummary(List joined) { + List titles = new ArrayList(); + CharSequence[] entryTitle = getEntries(); + for (CharSequence item : joined) { + int ix = getValueIndex(item); + titles.add((String) entryTitle[ix]); + } + return join(titles, ", "); + } + + public int getValueIndex(CharSequence item) { + CharSequence[] entryValues = getEntryValues(); + for (int i = 0; i < entryValues.length; i++) { + if (entryValues[i].equals(item)) { + return i; + } + } + throw new IllegalStateException(item + " not found in value list"); + } + + public int getValueTitleIndex(CharSequence item) { + CharSequence[] entries = getEntries(); + for (int i = 0; i < entries.length; i++) { + if (entries[i].equals(item)) { + return i; + } + } + throw new IllegalStateException(item + " not found in value title list"); + } + + private CharSequence[] restoreEntries() { + + ArrayList orderedList = new ArrayList(); + + // Initially populated with all of the values in the determined list. + CharSequence[] values = decodeValue(getValue(), mSeparator); + for (int ix = 0; ix < values.length; ix++) { + CharSequence value = values[ix]; + orderedList.add(value); + mListView.setItemChecked(ix, true); + } + + // This loop sets the default states, and adds to the name list if not + // on the list. + for (CharSequence value : getEntryValues()) { + entryChecked.put(value, false); + if (!orderedList.contains(value)) { + orderedList.add(value); + } + } + for (CharSequence value : orderedList) { + if (entryChecked.containsKey(value)) { + entryChecked.put(value, true); + } else { + throw new IllegalArgumentException("Invalid value " + value + + " in key list"); + } + } + return orderedList.toArray(new CharSequence[0]); + } + + /** + * Joins array of object to single string by separator + * + * Credits to kurellajunior on this post + * http://snippets.dzone.com/posts/show/91 + * + * @param iterable + * any kind of iterable ex.: ["a", "b", "c"] + * @param separator + * Separates entries ex.: "," + * @return joined string ex.: "a,b,c" + */ + protected static String join(Iterable iterable, String separator) { + Iterator oIter; + if (iterable == null || (!(oIter = iterable.iterator()).hasNext())) + return ""; + StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next())); + while (oIter.hasNext()) + oBuilder.append(separator).append(oIter.next()); + return oBuilder.toString(); + } +} diff --git a/library/src/com/mobeta/android/dslv/DragSortListView.java b/library/src/com/mobeta/android/dslv/DragSortListView.java index ed45637..a90786e 100644 --- a/library/src/com/mobeta/android/dslv/DragSortListView.java +++ b/library/src/com/mobeta/android/dslv/DragSortListView.java @@ -640,7 +640,7 @@ public ListAdapter getInputAdapter() { } } - private class AdapterWrapper implements WrapperListAdapter { + private class AdapterWrapper extends BaseAdapter implements WrapperListAdapter { private ListAdapter mAdapter; public AdapterWrapper(ListAdapter adapter) {