aboutsummaryrefslogtreecommitdiffstats
path: root/libraries
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2013-12-31 01:41:21 +0100
committerDominik Schürmann <dominik@dominikschuermann.de>2013-12-31 01:41:21 +0100
commitcdb3e04b4701079cb77fb74e57de7545431a3b37 (patch)
treea998fd4ba6e4cc890ded783cd52fb02e032df96b /libraries
parentdaadc30044659bc08ba3fcbca16083b0e98d353e (diff)
downloadopen-keychain-cdb3e04b4701079cb77fb74e57de7545431a3b37.tar.gz
open-keychain-cdb3e04b4701079cb77fb74e57de7545431a3b37.tar.bz2
open-keychain-cdb3e04b4701079cb77fb74e57de7545431a3b37.zip
Add pinned listview lib
Diffstat (limited to 'libraries')
-rw-r--r--libraries/pinned-section-listview/.gitignore6
-rw-r--r--libraries/pinned-section-listview/README.md75
-rw-r--r--libraries/pinned-section-listview/example/AndroidManifest.xml27
-rw-r--r--libraries/pinned-section-listview/example/assets/.gitignore1
-rw-r--r--libraries/pinned-section-listview/example/ic_launcher-web.pngbin0 -> 45500 bytes
-rw-r--r--libraries/pinned-section-listview/example/libs/android-support-v4.jarbin0 -> 393154 bytes
-rw-r--r--libraries/pinned-section-listview/example/proguard-project.txt20
-rw-r--r--libraries/pinned-section-listview/example/project.properties15
-rw-r--r--libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.pngbin0 -> 5819 bytes
-rw-r--r--libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.pngbin0 -> 3479 bytes
-rw-r--r--libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.pngbin0 -> 7534 bytes
-rw-r--r--libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 11732 bytes
-rw-r--r--libraries/pinned-section-listview/example/res/layout/activity_main.xml8
-rw-r--r--libraries/pinned-section-listview/example/res/menu/main.xml27
-rw-r--r--libraries/pinned-section-listview/example/res/values-v11/styles.xml11
-rw-r--r--libraries/pinned-section-listview/example/res/values-v14/styles.xml12
-rw-r--r--libraries/pinned-section-listview/example/res/values/colors.xml10
-rw-r--r--libraries/pinned-section-listview/example/res/values/strings.xml10
-rw-r--r--libraries/pinned-section-listview/example/res/values/styles.xml20
-rw-r--r--libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java285
-rw-r--r--libraries/pinned-section-listview/library/AndroidManifest.xml13
-rw-r--r--libraries/pinned-section-listview/library/assets/.gitignore1
-rw-r--r--libraries/pinned-section-listview/library/build.gradle24
-rw-r--r--libraries/pinned-section-listview/library/proguard-project.txt20
-rw-r--r--libraries/pinned-section-listview/library/project.properties15
-rw-r--r--libraries/pinned-section-listview/library/res/.gitignore1
-rw-r--r--libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java513
-rw-r--r--libraries/pinned-section-listview/screen1.pngbin0 -> 23124 bytes
-rw-r--r--libraries/pinned-section-listview/screen2.pngbin0 -> 22743 bytes
-rw-r--r--libraries/pinned-section-listview/screen3.pngbin0 -> 22885 bytes
30 files changed, 1114 insertions, 0 deletions
diff --git a/libraries/pinned-section-listview/.gitignore b/libraries/pinned-section-listview/.gitignore
new file mode 100644
index 000000000..ba354bcae
--- /dev/null
+++ b/libraries/pinned-section-listview/.gitignore
@@ -0,0 +1,6 @@
+bin
+gen
+.project
+.classpath
+.settings
+.DS_Store
diff --git a/libraries/pinned-section-listview/README.md b/libraries/pinned-section-listview/README.md
new file mode 100644
index 000000000..e89cde3bb
--- /dev/null
+++ b/libraries/pinned-section-listview/README.md
@@ -0,0 +1,75 @@
+Introduction
+============
+
+Easy to use ListView with pinned sections for Android. Pinned section is a header view which sticks to the top
+of the list until at least one item of that section is visible.
+
+![Alt text](screen1.png)&nbsp;
+![Alt text](screen2.png)&nbsp;
+![Alt text](screen3.png)
+
+Features
+========
+This list properly implements many features which are missing from other implementations. These are
+ * Fast scroll
+ * Headers and footers
+ * Clickable pinned sections
+
+Besides this it doesn't create any unnecessary views, layouts etc. It's really lean.
+
+Watch [this video][1] to see `PinnedSectionListView` in action.
+
+Usage
+=====
+ 1. Replace standard `ListView` with `com.hb.views.PinnedSectionListView` in your `layout.xml` file.
+
+ <com.hb.views.PinnedSectionListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ 2. Extend your `ListAdapter` in a way that it implements `PinnedSectionListAdapter` interface, in addition to
+ what it already implements. Basically you need to add a single `isItemViewTypePinned(int viewType)`
+ method. This method must return `true` for all view types which have to be pinned.
+
+ // Our adapter class implements 'PinnedSectionListAdapter' interface
+ class MyPinnedSectionListAdapter extends BaseAdapter implements PinnedSectionListAdapter {
+
+ ...
+
+ // We implement this method to return 'true' for all view types we want to pin
+ @Override
+ public boolean isItemViewTypePinned(int viewType) {
+ return viewType == <type to be pinned>;
+ }
+ }
+
+That's all. You are done! A working example can also be found in `example` folder.
+
+Used by
+=======
+Let us know if you use this library in your application and we will mention it here.
+
+[Grocery Sum][2]
+
+License
+=======
+
+ Copyright 2013 Sergej Shafarenka, halfbit.de
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+[1]: http://www.youtube.com/watch?v=mI3DpuoIIhQ
+[2]: https://play.google.com/store/apps/details?id=org.codechimp.grocerysum
diff --git a/libraries/pinned-section-listview/example/AndroidManifest.xml b/libraries/pinned-section-listview/example/AndroidManifest.xml
new file mode 100644
index 000000000..56621893d
--- /dev/null
+++ b/libraries/pinned-section-listview/example/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.hb.examples.pinnedsection"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="19" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+
+ <activity
+ android:name="com.hb.examples.PinnedSectionListActivity"
+ 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> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/assets/.gitignore b/libraries/pinned-section-listview/example/assets/.gitignore
new file mode 100644
index 000000000..fdffa2a0f
--- /dev/null
+++ b/libraries/pinned-section-listview/example/assets/.gitignore
@@ -0,0 +1 @@
+# placeholder
diff --git a/libraries/pinned-section-listview/example/ic_launcher-web.png b/libraries/pinned-section-listview/example/ic_launcher-web.png
new file mode 100644
index 000000000..8bd40412e
--- /dev/null
+++ b/libraries/pinned-section-listview/example/ic_launcher-web.png
Binary files differ
diff --git a/libraries/pinned-section-listview/example/libs/android-support-v4.jar b/libraries/pinned-section-listview/example/libs/android-support-v4.jar
new file mode 100644
index 000000000..65ebaf8dc
--- /dev/null
+++ b/libraries/pinned-section-listview/example/libs/android-support-v4.jar
Binary files differ
diff --git a/libraries/pinned-section-listview/example/proguard-project.txt b/libraries/pinned-section-listview/example/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libraries/pinned-section-listview/example/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# 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 *;
+#}
diff --git a/libraries/pinned-section-listview/example/project.properties b/libraries/pinned-section-listview/example/project.properties
new file mode 100644
index 000000000..1561d7a9a
--- /dev/null
+++ b/libraries/pinned-section-listview/example/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
+android.library.reference.1=../library
diff --git a/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..3841c3408
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..e65953cce
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..4ea093d86
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..87e928a35
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/libraries/pinned-section-listview/example/res/layout/activity_main.xml b/libraries/pinned-section-listview/example/res/layout/activity_main.xml
new file mode 100644
index 000000000..b641f4c80
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/layout/activity_main.xml
@@ -0,0 +1,8 @@
+<com.hb.views.PinnedSectionListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:headerDividersEnabled="false"
+ android:footerDividersEnabled="false"
+ android:divider="@null"
+ /> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/menu/main.xml b/libraries/pinned-section-listview/example/res/menu/main.xml
new file mode 100644
index 000000000..091b6c770
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/menu/main.xml
@@ -0,0 +1,27 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/action_fastscroll"
+ android:showAsAction="never"
+ android:title="@string/action_fastscroll"
+ android:checkable="true"/>
+
+ <item
+ android:id="@+id/action_addpadding"
+ android:showAsAction="never"
+ android:title="@string/action_addpadding"
+ android:checkable="true"/>
+
+ <item
+ android:id="@+id/action_showShadow"
+ android:showAsAction="never"
+ android:title="@string/action_showShadow"
+ android:checkable="true"/>
+
+ <item
+ android:id="@+id/action_showHeaderAndFooter"
+ android:showAsAction="never"
+ android:title="@string/action_showHeaderAndFooter"
+ android:checkable="true"/>
+
+</menu> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values-v11/styles.xml b/libraries/pinned-section-listview/example/res/values-v11/styles.xml
new file mode 100644
index 000000000..541752f6e
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values-v14/styles.xml b/libraries/pinned-section-listview/example/res/values-v14/styles.xml
new file mode 100644
index 000000000..f20e01501
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/colors.xml b/libraries/pinned-section-listview/example/res/values/colors.xml
new file mode 100644
index 000000000..b4da27c93
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="red_light">#ffff4444</color>
+ <color name="green_light">#ff99cc00</color>
+ <color name="orange_light">#ffffbb33</color>
+ <color name="blue_light">#ff33b5e5</color>
+
+ <!-- Went to: $android-sdk > Android:v14 > res > values > colors.xml and copied the colors-->
+
+</resources> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/strings.xml b/libraries/pinned-section-listview/example/res/values/strings.xml
new file mode 100644
index 000000000..e9d43a4aa
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">Pinned Section Demo</string>
+ <string name="action_fastscroll">Fast scroll</string>
+ <string name="action_addpadding">Add padding</string>
+ <string name="action_showShadow">Show shadow</string>
+ <string name="action_showHeaderAndFooter">Show Header &amp; Footer</string>
+
+</resources> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/styles.xml b/libraries/pinned-section-listview/example/res/values/styles.xml
new file mode 100644
index 000000000..4a10ca492
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java b/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
new file mode 100644
index 000000000..223718a06
--- /dev/null
+++ b/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2013 Sergej Shafarenka, halfbit.de
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.hb.examples;
+
+import java.util.Locale;
+
+import android.annotation.SuppressLint;
+import android.app.ListActivity;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.hb.examples.pinnedsection.R;
+import com.hb.views.PinnedSectionListView;
+import com.hb.views.PinnedSectionListView.PinnedSectionListAdapter;
+
+public class PinnedSectionListActivity extends ListActivity implements OnClickListener {
+
+ static class SimpleAdapter extends ArrayAdapter<Item> implements PinnedSectionListAdapter {
+
+ private static final int[] COLORS = new int[] {
+ R.color.green_light, R.color.orange_light,
+ R.color.blue_light, R.color.red_light };
+
+ public SimpleAdapter(Context context, int resource, int textViewResourceId) {
+ super(context, resource, textViewResourceId);
+
+ final int sectionsNumber = 'Z' - 'A' + 1;
+ prepareSections(sectionsNumber);
+
+ int sectionPosition = 0, listPosition = 0;
+ for (char i=0; i<sectionsNumber; i++) {
+ Item section = new Item(Item.SECTION, String.valueOf((char)('A' + i)));
+ section.sectionPosition = sectionPosition;
+ section.listPosition = listPosition++;
+ onSectionAdded(section, sectionPosition);
+ add(section);
+
+ final int itemsNumber = (int) Math.abs((Math.cos(2f*Math.PI/3f * sectionsNumber / (i+1f)) * 25f));
+ for (int j=0;j<itemsNumber;j++) {
+ Item item = new Item(Item.ITEM, section.text.toUpperCase(Locale.ENGLISH) + " - " + j);
+ item.sectionPosition = sectionPosition;
+ item.listPosition = listPosition++;
+ add(item);
+ }
+
+ sectionPosition++;
+ }
+ }
+
+ protected void prepareSections(int sectionsNumber) { }
+ protected void onSectionAdded(Item section, int sectionPosition) { }
+
+ @Override public View getView(int position, View convertView, ViewGroup parent) {
+ TextView view = (TextView) super.getView(position, convertView, parent);
+ view.setTextColor(Color.DKGRAY);
+ view.setTag("" + position);
+ Item item = getItem(position);
+ if (item.type == Item.SECTION) {
+ //view.setOnClickListener(PinnedSectionListActivity.this);
+ view.setBackgroundColor(parent.getResources().getColor(COLORS[item.sectionPosition % COLORS.length]));
+ }
+ return view;
+ }
+
+ @Override public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override public int getItemViewType(int position) {
+ return getItem(position).type;
+ }
+
+ @Override
+ public boolean isItemViewTypePinned(int viewType) {
+ return viewType == Item.SECTION;
+ }
+
+ }
+
+ static class FastScrollAdapter extends SimpleAdapter implements SectionIndexer {
+
+ private Item[] sections;
+
+ public FastScrollAdapter(Context context, int resource, int textViewResourceId) {
+ super(context, resource, textViewResourceId);
+ }
+
+ @Override protected void prepareSections(int sectionsNumber) {
+ sections = new Item[sectionsNumber];
+ }
+
+ @Override protected void onSectionAdded(Item section, int sectionPosition) {
+ sections[sectionPosition] = section;
+ }
+
+ @Override public Item[] getSections() {
+ return sections;
+ }
+
+ @Override public int getPositionForSection(int section) {
+ if (section >= sections.length) {
+ section = sections.length - 1;
+ }
+ return sections[section].listPosition;
+ }
+
+ @Override public int getSectionForPosition(int position) {
+ if (position >= getCount()) {
+ position = getCount() - 1;
+ }
+ return getItem(position).sectionPosition;
+ }
+
+ }
+
+ static class Item {
+
+ public static final int ITEM = 0;
+ public static final int SECTION = 1;
+
+ public final int type;
+ public final String text;
+
+ public int sectionPosition;
+ public int listPosition;
+
+ public Item(int type, String text) {
+ this.type = type;
+ this.text = text;
+ }
+
+ @Override public String toString() {
+ return text;
+ }
+
+ }
+
+ private boolean hasHeaderAndFooter;
+ private boolean isFastScroll;
+ private boolean addPadding;
+ private boolean isShadowVisible = true;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ if (savedInstanceState != null) {
+ isFastScroll = savedInstanceState.getBoolean("isFastScroll");
+ addPadding = savedInstanceState.getBoolean("addPadding");
+ isShadowVisible = savedInstanceState.getBoolean("isShadowVisible");
+ hasHeaderAndFooter = savedInstanceState.getBoolean("hasHeaderAndFooter");
+ }
+ initializeHeaderAndFooter();
+ initializeAdapter();
+ initializePadding();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean("isFastScroll", isFastScroll);
+ outState.putBoolean("addPadding", addPadding);
+ outState.putBoolean("isShadowVisible", isShadowVisible);
+ outState.putBoolean("hasHeaderAndFooter", hasHeaderAndFooter);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Item item = (Item) getListView().getAdapter().getItem(position);
+ if (item != null) {
+ Toast.makeText(this, "Item " + position + ": " + item.text, Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Item " + position, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.main, menu);
+ menu.getItem(0).setChecked(isFastScroll);
+ menu.getItem(1).setChecked(addPadding);
+ menu.getItem(2).setChecked(isShadowVisible);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_fastscroll:
+ isFastScroll = !isFastScroll;
+ item.setChecked(isFastScroll);
+ initializeAdapter();
+ break;
+ case R.id.action_addpadding:
+ addPadding = !addPadding;
+ item.setChecked(addPadding);
+ initializePadding();
+ break;
+ case R.id.action_showShadow:
+ isShadowVisible = !isShadowVisible;
+ item.setChecked(isShadowVisible);
+ ((PinnedSectionListView)getListView()).setShadowVisible(isShadowVisible);
+ break;
+ case R.id.action_showHeaderAndFooter:
+ hasHeaderAndFooter = !hasHeaderAndFooter;
+ item.setChecked(hasHeaderAndFooter);
+ initializeHeaderAndFooter();
+ break;
+ }
+ return true;
+ }
+
+ private void initializePadding() {
+ float density = getResources().getDisplayMetrics().density;
+ int padding = addPadding ? (int) (16 * density) : 0;
+ getListView().setPadding(padding, padding, padding, padding);
+ }
+
+ private void initializeHeaderAndFooter() {
+ setListAdapter(null);
+ if (hasHeaderAndFooter) {
+ ListView list = getListView();
+
+ LayoutInflater inflater = LayoutInflater.from(this);
+ TextView header1 = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
+ header1.setText("First header");
+ list.addHeaderView(header1);
+
+ TextView header2 = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
+ header2.setText("Second header");
+ list.addHeaderView(header2);
+
+ TextView footer = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
+ footer.setText("Single footer");
+ list.addFooterView(footer);
+ }
+ initializeAdapter();
+ }
+
+ @SuppressLint("NewApi")
+ private void initializeAdapter() {
+ getListView().setFastScrollEnabled(isFastScroll);
+ if (isFastScroll) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ getListView().setFastScrollAlwaysVisible(true);
+ }
+ setListAdapter(new FastScrollAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1));
+ } else {
+ setListAdapter(new SimpleAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1));
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ Toast.makeText(this, "Item: " + v.getTag() , Toast.LENGTH_SHORT).show();
+ }
+
+} \ No newline at end of file
diff --git a/libraries/pinned-section-listview/library/AndroidManifest.xml b/libraries/pinned-section-listview/library/AndroidManifest.xml
new file mode 100644
index 000000000..2e2ee9173
--- /dev/null
+++ b/libraries/pinned-section-listview/library/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.hb.views.pinnedsection"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="7"
+ android:targetSdkVersion="17" />
+
+ <application android:allowBackup="false" />
+
+</manifest> \ No newline at end of file
diff --git a/libraries/pinned-section-listview/library/assets/.gitignore b/libraries/pinned-section-listview/library/assets/.gitignore
new file mode 100644
index 000000000..fdffa2a0f
--- /dev/null
+++ b/libraries/pinned-section-listview/library/assets/.gitignore
@@ -0,0 +1 @@
+# placeholder
diff --git a/libraries/pinned-section-listview/library/build.gradle b/libraries/pinned-section-listview/library/build.gradle
new file mode 100644
index 000000000..d77f4746f
--- /dev/null
+++ b/libraries/pinned-section-listview/library/build.gradle
@@ -0,0 +1,24 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.6.3'
+ }
+}
+
+apply plugin: 'android-library'
+
+
+android {
+ compileSdkVersion 17
+ buildToolsVersion '19'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
diff --git a/libraries/pinned-section-listview/library/proguard-project.txt b/libraries/pinned-section-listview/library/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libraries/pinned-section-listview/library/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# 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 *;
+#}
diff --git a/libraries/pinned-section-listview/library/project.properties b/libraries/pinned-section-listview/library/project.properties
new file mode 100644
index 000000000..484dab075
--- /dev/null
+++ b/libraries/pinned-section-listview/library/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
+android.library=true
diff --git a/libraries/pinned-section-listview/library/res/.gitignore b/libraries/pinned-section-listview/library/res/.gitignore
new file mode 100644
index 000000000..fdffa2a0f
--- /dev/null
+++ b/libraries/pinned-section-listview/library/res/.gitignore
@@ -0,0 +1 @@
+# placeholder
diff --git a/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java b/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
new file mode 100644
index 000000000..8100ad693
--- /dev/null
+++ b/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2013 Sergej Shafarenka, halfbit.de
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file kt in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.hb.views;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.GradientDrawable.Orientation;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.AbsListView;
+import android.widget.HeaderViewListAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+
+import com.hb.views.pinnedsection.BuildConfig;
+
+/**
+ * ListView, which is capable to pin section views at its top while the rest is still scrolled.
+ */
+public class PinnedSectionListView extends ListView {
+
+ //-- inner classes
+
+ /** List adapter to be implemented for being used with PinnedSectionListView adapter. */
+ public static interface PinnedSectionListAdapter extends ListAdapter {
+ /** This method shall return 'true' if views of given type has to be pinned. */
+ boolean isItemViewTypePinned(int viewType);
+ }
+
+ /** Wrapper class for pinned section view and its position in the list. */
+ static class PinnedSection {
+ public View view;
+ public int position;
+ public long id;
+ }
+
+ //-- class fields
+
+ // fields used for handling touch events
+ private final Rect mTouchRect = new Rect();
+ private final PointF mTouchPoint = new PointF();
+ private int mTouchSlop;
+ private View mTouchTarget;
+ private MotionEvent mDownEvent;
+
+ // fields used for drawing shadow under a pinned section
+ private GradientDrawable mShadowDrawable;
+ private int mSectionsDistanceY;
+ private int mShadowHeight;
+
+ /** Delegating listener, can be null. */
+ OnScrollListener mDelegateOnScrollListener;
+
+ /** Shadow for being recycled, can be null. */
+ PinnedSection mRecycleSection;
+
+ /** shadow instance with a pinned view, can be null. */
+ PinnedSection mPinnedSection;
+
+ /** Pinned view Y-translation. We use it to stick pinned view to the next section. */
+ int mTranslateY;
+
+ /** Scroll listener which does the magic */
+ private final OnScrollListener mOnScrollListener = new OnScrollListener() {
+
+ @Override public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (mDelegateOnScrollListener != null) { // delegate
+ mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+
+ if (mDelegateOnScrollListener != null) { // delegate
+ mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+
+ // get expected adapter or fail fast
+ ListAdapter adapter = getAdapter();
+ if (adapter == null || visibleItemCount == 0) return; // nothing to do
+
+ final boolean isFirstVisibleItemSection =
+ isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
+
+ if (isFirstVisibleItemSection) {
+ View sectionView = getChildAt(0);
+ if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
+ destroyPinnedShadow();
+ } else { // section doesn't stick to the top, make sure we have a pinned shadow
+ ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
+ }
+
+ } else { // section is not at the first visible position
+ int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
+ if (sectionPosition > -1) { // we have section position
+ ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
+ } else { // there is no section for the first visible item, destroy shadow
+ destroyPinnedShadow();
+ }
+ }
+ };
+
+ };
+
+ /** Default change observer. */
+ private final DataSetObserver mDataSetObserver = new DataSetObserver() {
+ @Override public void onChanged() {
+ recreatePinnedShadow();
+ };
+ @Override public void onInvalidated() {
+ recreatePinnedShadow();
+ }
+ };
+
+ //-- constructors
+
+ public PinnedSectionListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView();
+ }
+
+ public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initView();
+ }
+
+ private void initView() {
+ setOnScrollListener(mOnScrollListener);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ initShadow(true);
+ }
+
+ //-- public API methods
+
+ public void setShadowVisible(boolean visible) {
+ initShadow(visible);
+ if (mPinnedSection != null) {
+ View v = mPinnedSection.view;
+ invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
+ }
+ }
+
+ //-- pinned section drawing methods
+
+ public void initShadow(boolean visible) {
+ if (visible) {
+ if (mShadowDrawable == null) {
+ mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
+ new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
+ mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
+ }
+ } else {
+ if (mShadowDrawable != null) {
+ mShadowDrawable = null;
+ mShadowHeight = 0;
+ }
+ }
+ }
+
+ /** Create shadow wrapper with a pinned view for a view at given position */
+ void createPinnedShadow(int position) {
+
+ // try to recycle shadow
+ PinnedSection pinnedShadow = mRecycleSection;
+ mRecycleSection = null;
+
+ // create new shadow, if needed
+ if (pinnedShadow == null) pinnedShadow = new PinnedSection();
+ // request new view using recycled view, if such
+ View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
+
+ // read layout parameters
+ LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
+ if (layoutParams == null) { // create default layout params
+ layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ int heightMode = MeasureSpec.getMode(layoutParams.height);
+ int heightSize = MeasureSpec.getSize(layoutParams.height);
+
+ if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
+
+ int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
+ if (heightSize > maxHeight) heightSize = maxHeight;
+
+ // measure & layout
+ int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
+ int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
+ pinnedView.measure(ws, hs);
+ pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
+ mTranslateY = 0;
+
+ // initialize pinned shadow
+ pinnedShadow.view = pinnedView;
+ pinnedShadow.position = position;
+ pinnedShadow.id = getAdapter().getItemId(position);
+
+ // store pinned shadow
+ mPinnedSection = pinnedShadow;
+ }
+
+ /** Destroy shadow wrapper for currently pinned view */
+ void destroyPinnedShadow() {
+ if (mPinnedSection != null) {
+ // keep shadow for being recycled later
+ mRecycleSection = mPinnedSection;
+ mPinnedSection = null;
+ }
+ }
+
+ /** Makes sure we have an actual pinned shadow for given position. */
+ void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
+ if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
+ destroyPinnedShadow();
+ return;
+ }
+
+ if (mPinnedSection != null
+ && mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
+ destroyPinnedShadow();
+ }
+
+ if (mPinnedSection == null) { // create shadow, if empty
+ createPinnedShadow(sectionPosition);
+ }
+
+ // align shadow according to next section position, if needed
+ int nextPosition = sectionPosition + 1;
+ if (nextPosition < getCount()) {
+ int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
+ visibleItemCount - (nextPosition - firstVisibleItem));
+ if (nextSectionPosition > -1) {
+ View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
+ final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
+ mSectionsDistanceY = nextSectionView.getTop() - bottom;
+ if (mSectionsDistanceY < 0) {
+ // next section overlaps pinned shadow, move it up
+ mTranslateY = mSectionsDistanceY;
+ } else {
+ // next section does not overlap with pinned, stick to top
+ mTranslateY = 0;
+ }
+ } else {
+ // no other sections are visible, stick to top
+ mTranslateY = 0;
+ mSectionsDistanceY = Integer.MAX_VALUE;
+ }
+ }
+
+ }
+
+ int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
+ ListAdapter adapter = getAdapter();
+ for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
+ int position = firstVisibleItem + childIndex;
+ int viewType = adapter.getItemViewType(position);
+ if (isItemViewTypePinned(adapter, viewType)) return position;
+ }
+ return -1;
+ }
+
+ int findCurrentSectionPosition(int fromPosition) {
+ ListAdapter adapter = getAdapter();
+
+ if (adapter instanceof SectionIndexer) {
+ // try fast way by asking section indexer
+ SectionIndexer indexer = (SectionIndexer) adapter;
+ int sectionPosition = indexer.getSectionForPosition(fromPosition);
+ int itemPosition = indexer.getPositionForSection(sectionPosition);
+ int typeView = adapter.getItemViewType(itemPosition);
+ if (isItemViewTypePinned(adapter, typeView)) {
+ return itemPosition;
+ } // else, no luck
+ }
+
+ // try slow way by looking through to the next section item above
+ for (int position=fromPosition; position>=0; position--) {
+ int viewType = adapter.getItemViewType(position);
+ if (isItemViewTypePinned(adapter, viewType)) return position;
+ }
+ return -1; // no candidate found
+ }
+
+ void recreatePinnedShadow() {
+ destroyPinnedShadow();
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && adapter.getCount() > 0) {
+ int firstVisiblePosition = getFirstVisiblePosition();
+ int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
+ if (sectionPosition == -1) return; // no views to pin, exit
+ ensureShadowForPosition(sectionPosition,
+ firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
+ }
+ }
+
+ @Override
+ public void setOnScrollListener(OnScrollListener listener) {
+ if (listener == mOnScrollListener) {
+ super.setOnScrollListener(listener);
+ } else {
+ mDelegateOnScrollListener = listener;
+ }
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ super.onRestoreInstanceState(state);
+ post(new Runnable() {
+ @Override public void run() { // restore pinned view after configuration change
+ recreatePinnedShadow();
+ }
+ });
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+
+ // assert adapter in debug mode
+ if (BuildConfig.DEBUG && adapter != null) {
+ if (!(adapter instanceof PinnedSectionListAdapter))
+ throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
+ if (adapter.getViewTypeCount() < 2)
+ throw new IllegalArgumentException("Does your adapter handle at least two types" +
+ " of views in getViewTypeCount() method: items and sections?");
+ }
+
+ // unregister observer at old adapter and register on new one
+ ListAdapter oldAdapter = getAdapter();
+ if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
+ if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
+
+ // destroy pinned shadow, if new adapter is not same as old one
+ if (oldAdapter != adapter) destroyPinnedShadow();
+
+ super.setAdapter(adapter);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ if (mPinnedSection != null) {
+ int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
+ int shadowWidth = mPinnedSection.view.getWidth();
+ if (parentWidth != shadowWidth) {
+ recreatePinnedShadow();
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ if (mPinnedSection != null) {
+
+ // prepare variables
+ int pLeft = getListPaddingLeft();
+ int pTop = getListPaddingTop();
+ View view = mPinnedSection.view;
+
+ // draw child
+ canvas.save();
+
+ int clipHeight = view.getHeight() +
+ (mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
+ canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
+
+ canvas.translate(pLeft, pTop + mTranslateY);
+ drawChild(canvas, mPinnedSection.view, getDrawingTime());
+
+ if (mShadowDrawable != null && mSectionsDistanceY > 0) {
+ mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
+ mPinnedSection.view.getBottom(),
+ mPinnedSection.view.getRight(),
+ mPinnedSection.view.getBottom() + mShadowHeight);
+ mShadowDrawable.draw(canvas);
+ }
+
+ canvas.restore();
+ }
+ }
+
+ //-- touch handling methods
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+
+ final float x = ev.getX();
+ final float y = ev.getY();
+ final int action = ev.getAction();
+
+ if (action == MotionEvent.ACTION_DOWN
+ && mTouchTarget == null
+ && mPinnedSection != null
+ && isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
+
+ // user touched pinned view
+ mTouchTarget = mPinnedSection.view;
+ mTouchPoint.x = x;
+ mTouchPoint.y = y;
+
+ // copy down event for eventually be used later
+ mDownEvent = MotionEvent.obtain(ev);
+ }
+
+ if (mTouchTarget != null) {
+ if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
+ mTouchTarget.dispatchTouchEvent(ev);
+ }
+
+ if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
+ super.dispatchTouchEvent(ev);
+ performPinnedItemClick();
+ clearTouchTarget();
+
+ } else if (action == MotionEvent.ACTION_CANCEL) { // cancel
+ clearTouchTarget();
+
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
+
+ // cancel sequence on touch target
+ MotionEvent event = MotionEvent.obtain(ev);
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ mTouchTarget.dispatchTouchEvent(event);
+ event.recycle();
+
+ // provide correct sequence to super class for further handling
+ super.dispatchTouchEvent(mDownEvent);
+ super.dispatchTouchEvent(ev);
+ clearTouchTarget();
+
+ }
+ }
+
+ return true;
+ }
+
+ // call super if this was not our pinned view
+ return super.dispatchTouchEvent(ev);
+ }
+
+ private boolean isPinnedViewTouched(View view, float x, float y) {
+ view.getHitRect(mTouchRect);
+
+ // by taping top or bottom padding, the list performs on click on a border item.
+ // we don't add top padding here to keep behavior consistent.
+ mTouchRect.top += mTranslateY;
+
+ mTouchRect.bottom += mTranslateY + getPaddingTop();
+ mTouchRect.left += getPaddingLeft();
+ mTouchRect.right -= getPaddingRight();
+ return mTouchRect.contains((int)x, (int)y);
+ }
+
+ private void clearTouchTarget() {
+ mTouchTarget = null;
+ if (mDownEvent != null) {
+ mDownEvent.recycle();
+ mDownEvent = null;
+ }
+ }
+
+ private boolean performPinnedItemClick() {
+ if (mPinnedSection == null) return false;
+
+ OnItemClickListener listener = getOnItemClickListener();
+ if (listener != null) {
+ View view = mPinnedSection.view;
+ playSoundEffect(SoundEffectConstants.CLICK);
+ if (view != null) {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ }
+ listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
+ if (adapter instanceof HeaderViewListAdapter) {
+ adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
+ }
+ return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
+ }
+
+}
diff --git a/libraries/pinned-section-listview/screen1.png b/libraries/pinned-section-listview/screen1.png
new file mode 100644
index 000000000..9cca20109
--- /dev/null
+++ b/libraries/pinned-section-listview/screen1.png
Binary files differ
diff --git a/libraries/pinned-section-listview/screen2.png b/libraries/pinned-section-listview/screen2.png
new file mode 100644
index 000000000..ad59f6f2f
--- /dev/null
+++ b/libraries/pinned-section-listview/screen2.png
Binary files differ
diff --git a/libraries/pinned-section-listview/screen3.png b/libraries/pinned-section-listview/screen3.png
new file mode 100644
index 000000000..0fd528fa9
--- /dev/null
+++ b/libraries/pinned-section-listview/screen3.png
Binary files differ