├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── linearlistview ├── build.gradle ├── deploy.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── linearlistview │ │ ├── LinearListView.java │ │ └── internal │ │ └── IcsLinearLayout.java │ └── res │ └── values │ └── attrs.xml ├── sample ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── linearlistview │ │ └── MainActivity.java │ └── res │ ├── drawable-hdpi │ ├── divider_vertical_holo_dark.9.png │ ├── ic_action_search.png │ └── ic_launcher.png │ ├── drawable-ldpi │ ├── divider_vertical_holo_dark.9.png │ └── ic_launcher.png │ ├── drawable-mdpi │ ├── divider_vertical_holo_dark.9.png │ ├── ic_action_search.png │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── divider_vertical_holo_dark.9.png │ └── ic_launcher.png │ ├── layout │ ├── activity_main.xml │ └── list_item.xml │ ├── menu │ └── main.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw720dp-land │ └── dimens.xml │ ├── values-v11 │ └── styles.xml │ ├── values-v14 │ └── styles.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | build/ 16 | 17 | # Local configuration file (sdk path, etc) 18 | local.properties 19 | 20 | .project 21 | .classpath 22 | .settings 23 | target/ 24 | libs/ 25 | classes/ 26 | gen-external-apklibs/ 27 | tmp/ 28 | 29 | # IDEA Ignores 30 | *.iml 31 | *.ipr 32 | *.iws 33 | .idea/ 34 | .gradle/ 35 | 36 | # MAC 37 | *.DS_Store 38 | 39 | *.orig 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LinearListView 2 | ================= 3 | 4 | Android library that allows you to bind a `LinearLayout` with a `ListAdapter`. 5 | 6 | 7 | Download 8 | -------- 9 | 10 | Gradle: 11 | 12 | ```groovy 13 | dependencies { 14 | compile 'com.github.frankiesardo:linearlistview:1.0.1@aar' 15 | } 16 | ``` 17 | 18 | Usage 19 | ===== 20 | 21 | A simple example of the xml attributes that you can use 22 | 23 | ```xml 24 | 35 | ``` 36 | 37 | Where: `showDividers`, `divider` and `dividerPadding` have the same meaning of a `LinearLayout` (API 11) attributes, `entries` is the same as a `ListView` attribute and `dividerThickness` controls how thick is the divider (namely its height or width depending on its orientation). 38 | 39 | In your `Activity` you can call `myLinearListView.setAdapter(myAdapter)` and each view from your `ListAdapter` will be inflated and kept in sync with its data. 40 | 41 | 42 | Why you want to use a LinearListVew: 43 | 44 | * You need an horizontal scrollable list (since `Gallery` is deprecated): Just embed this layout with an `horizontal` orientation in an `HorizontalScrollView`. 45 | * You need two or more lists in a vertical scrollable view. 46 | * You need to inflate several views in a `LinearLayout` from an `Adapter` and you want to keep them synchronized with its data. This is especially useful when working with a `CursorAdapter`. 47 | * You need a list with a fixed height (the total height of its children). Or, you have a complex, scrollable layout and don't want to use a `ListView` with footers and headers. 48 | * You simply want to use a `LinearLayout` with dividers (API 11) with the added benefit of a `dividerThickness` attribute. 49 | 50 | 51 | Roadmap 52 | ------------------------- 53 | 54 | * Adding a `drawSelectorOnTop` attribute. 55 | 56 | 57 | License 58 | ======= 59 | 60 | Copyright 2012 Frankie Sardo 61 | 62 | Licensed under the Apache License, Version 2.0 (the "License"); 63 | you may not use this file except in compliance with the License. 64 | You may obtain a copy of the License at 65 | 66 | http://www.apache.org/licenses/LICENSE-2.0 67 | 68 | Unless required by applicable law or agreed to in writing, software 69 | distributed under the License is distributed on an "AS IS" BASIS, 70 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 71 | See the License for the specific language governing permissions and 72 | limitations under the License. 73 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Oct 21 17:20:11 BST 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.7-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /linearlistview/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:0.9.+' 7 | } 8 | } 9 | 10 | apply plugin: 'android-library' 11 | 12 | apply from: file("deploy.gradle") 13 | 14 | android { 15 | compileSdkVersion 18 16 | buildToolsVersion "19.0.0" 17 | } 18 | -------------------------------------------------------------------------------- /linearlistview/deploy.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | apply plugin: 'signing' 3 | 4 | version = "1.0.1" 5 | 6 | def isReleaseBuild() { 7 | !version.contains("SNAPSHOT") 8 | } 9 | 10 | def sonatypeRepositoryUrl 11 | 12 | if (isReleaseBuild()) { 13 | println "RELEASE BUILD $version" 14 | sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 15 | } else { 16 | println "DEBUG BUILD $version" 17 | sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/" 18 | } 19 | 20 | def username = project.hasProperty("nexusUsername") ? project.property("nexusUsername") : "" 21 | def password = project.hasProperty("nexusPassword") ? project.property("nexusPassword") : "" 22 | 23 | afterEvaluate { project -> 24 | uploadArchives { 25 | repositories { 26 | mavenDeployer { 27 | beforeDeployment { deployment -> signing.signPom(deployment) } 28 | 29 | repository(url: sonatypeRepositoryUrl) { 30 | authentication(userName: username, password: password) 31 | } 32 | 33 | pom.project { 34 | name 'LinearListView' 35 | packaging 'aar' 36 | artifactId 'linearlistview' 37 | groupId 'com.github.frankiesardo' 38 | url 'https://github.com/frankiesardo/LinearListView' 39 | description 'Android library that allows you to bind a LinearLayout with a ListAdapter' 40 | inceptionYear '2012' 41 | 42 | scm { 43 | url 'https://github.com/frankiesardo/LinearListView' 44 | connection 'scm:git:git://github.com/frankiesardo/LinearListView.git' 45 | developerConnection 'scm:git@github.com:novoda/LinearListView.git' 46 | } 47 | 48 | licenses { 49 | license { 50 | name 'The Apache Software License, Version 2.0' 51 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 52 | distribution 'repo' 53 | } 54 | } 55 | 56 | developers { 57 | developer { 58 | id 'frankiesardo' 59 | name 'Frankie Sardo' 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | 67 | signing { 68 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 69 | sign configurations.archives 70 | } 71 | 72 | task androidJavadocs(type: Javadoc) { 73 | source = android.sourceSets.main.allJava 74 | } 75 | 76 | task androidJavadocsJar(type: Jar) { 77 | classifier = 'javadoc' 78 | //basename = artifact_id 79 | from androidJavadocs.destinationDir 80 | } 81 | 82 | task androidSourcesJar(type: Jar) { 83 | classifier = 'sources' 84 | //basename = artifact_id 85 | from android.sourceSets.main.allSource 86 | } 87 | 88 | artifacts { 89 | //archives packageReleaseJar 90 | archives androidSourcesJar 91 | archives androidJavadocsJar 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /linearlistview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /linearlistview/src/main/java/com/linearlistview/LinearListView.java: -------------------------------------------------------------------------------- 1 | package com.linearlistview; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.database.DataSetObserver; 6 | import android.graphics.drawable.ColorDrawable; 7 | import android.graphics.drawable.Drawable; 8 | import android.util.AttributeSet; 9 | import android.view.SoundEffectConstants; 10 | import android.view.View; 11 | import android.widget.ArrayAdapter; 12 | import android.widget.ListAdapter; 13 | import android.widget.WrapperListAdapter; 14 | 15 | import com.linearlistview.internal.IcsLinearLayout; 16 | 17 | /** 18 | * An extension of a linear layout that supports the divider API of Android 19 | * 4.0+. You can populate this layout with data that comes from a 20 | * {@link android.widget.ListAdapter} 21 | */ 22 | public class LinearListView extends IcsLinearLayout { 23 | 24 | private static final int[] R_styleable_LinearListView = new int[] { 25 | /* 0 */android.R.attr.entries, 26 | /* 1 */R.attr.dividerThickness 27 | }; 28 | 29 | private static final int LinearListView_entries = 0; 30 | private static final int LinearListView_dividerThickness = 1; 31 | 32 | private View mEmptyView; 33 | private ListAdapter mAdapter; 34 | private boolean mAreAllItemsSelectable; 35 | private OnItemClickListener mOnItemClickListener; 36 | private DataSetObserver mDataObserver = new DataSetObserver() { 37 | 38 | @Override 39 | public void onChanged() { 40 | setupChildren(); 41 | } 42 | 43 | @Override 44 | public void onInvalidated() { 45 | setupChildren(); 46 | } 47 | 48 | }; 49 | 50 | public LinearListView(Context context) { 51 | this(context, null); 52 | } 53 | 54 | public LinearListView(Context context, AttributeSet attrs) { 55 | super(context, attrs); 56 | 57 | TypedArray a = context.obtainStyledAttributes(attrs, 58 | R_styleable_LinearListView); 59 | 60 | // Use the thickness specified, zero being the default 61 | final int thickness = a.getDimensionPixelSize( 62 | LinearListView_dividerThickness, 0); 63 | if (thickness != 0) { 64 | setDividerThickness(thickness); 65 | } 66 | 67 | CharSequence[] entries = a.getTextArray(LinearListView_entries); 68 | if (entries != null) { 69 | setAdapter(new ArrayAdapter(context, 70 | android.R.layout.simple_list_item_1, entries)); 71 | } 72 | 73 | a.recycle(); 74 | } 75 | 76 | @Override 77 | public void setOrientation(int orientation) { 78 | if (orientation != getOrientation()) { 79 | int tmp = mDividerHeight; 80 | mDividerHeight = mDividerWidth; 81 | mDividerWidth = tmp; 82 | } 83 | super.setOrientation(orientation); 84 | } 85 | 86 | /** 87 | * Set the divider thickness size in pixel. That means setting the divider 88 | * height if the layout has an HORIZONTAL orientation and setting the 89 | * divider width otherwise. 90 | * 91 | * @param thickness 92 | * The divider thickness in pixel. 93 | */ 94 | public void setDividerThickness(int thickness) { 95 | if (getOrientation() == VERTICAL) { 96 | mDividerHeight = thickness; 97 | } else { 98 | mDividerWidth = thickness; 99 | } 100 | requestLayout(); 101 | } 102 | 103 | public ListAdapter getAdapter() { 104 | return mAdapter; 105 | } 106 | 107 | /** 108 | * Sets the data behind this LinearListView. 109 | * 110 | * @param adapter 111 | * The ListAdapter which is responsible for maintaining the data 112 | * backing this list and for producing a view to represent an 113 | * item in that data set. 114 | * 115 | * @see #getAdapter() 116 | */ 117 | public void setAdapter(ListAdapter adapter) { 118 | if (mAdapter != null) { 119 | mAdapter.unregisterDataSetObserver(mDataObserver); 120 | } 121 | 122 | mAdapter = adapter; 123 | 124 | if (mAdapter != null) { 125 | mAdapter.registerDataSetObserver(mDataObserver); 126 | mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); 127 | } 128 | 129 | setupChildren(); 130 | 131 | } 132 | 133 | /** 134 | * Interface definition for a callback to be invoked when an item in this 135 | * LinearListView has been clicked. 136 | */ 137 | public interface OnItemClickListener { 138 | 139 | /** 140 | * Callback method to be invoked when an item in this LinearListView has 141 | * been clicked. 142 | *

143 | * Implementers can call getItemAtPosition(position) if they need to 144 | * access the data associated with the selected item. 145 | * 146 | * @param parent 147 | * The LinearListView where the click happened. 148 | * @param view 149 | * The view within the LinearListView that was clicked (this 150 | * will be a view provided by the adapter) 151 | * @param position 152 | * The position of the view in the adapter. 153 | * @param id 154 | * The row id of the item that was clicked. 155 | */ 156 | void onItemClick(LinearListView parent, View view, int position, long id); 157 | } 158 | 159 | /** 160 | * Register a callback to be invoked when an item in this LinearListView has 161 | * been clicked. 162 | * 163 | * @param listener 164 | * The callback that will be invoked. 165 | */ 166 | public void setOnItemClickListener(OnItemClickListener listener) { 167 | mOnItemClickListener = listener; 168 | } 169 | 170 | /** 171 | * @return The callback to be invoked with an item in this LinearListView has 172 | * been clicked, or null id no callback has been set. 173 | */ 174 | public final OnItemClickListener getOnItemClickListener() { 175 | return mOnItemClickListener; 176 | } 177 | 178 | /** 179 | * Call the OnItemClickListener, if it is defined. 180 | * 181 | * @param view 182 | * The view within the LinearListView that was clicked. 183 | * @param position 184 | * The position of the view in the adapter. 185 | * @param id 186 | * The row id of the item that was clicked. 187 | * @return True if there was an assigned OnItemClickListener that was 188 | * called, false otherwise is returned. 189 | */ 190 | public boolean performItemClick(View view, int position, long id) { 191 | if (mOnItemClickListener != null) { 192 | playSoundEffect(SoundEffectConstants.CLICK); 193 | mOnItemClickListener.onItemClick(this, view, position, id); 194 | return true; 195 | } 196 | 197 | return false; 198 | } 199 | 200 | /** 201 | * Sets the view to show if the adapter is empty 202 | */ 203 | public void setEmptyView(View emptyView) { 204 | mEmptyView = emptyView; 205 | 206 | final ListAdapter adapter = getAdapter(); 207 | final boolean empty = ((adapter == null) || adapter.isEmpty()); 208 | updateEmptyStatus(empty); 209 | } 210 | 211 | /** 212 | * When the current adapter is empty, the LinearListView can display a special 213 | * view call the empty view. The empty view is used to provide feedback to 214 | * the user that no data is available in this LinearListView. 215 | * 216 | * @return The view to show if the adapter is empty. 217 | */ 218 | public View getEmptyView() { 219 | return mEmptyView; 220 | } 221 | 222 | /** 223 | * Update the status of the list based on the empty parameter. If empty is 224 | * true and we have an empty view, display it. In all the other cases, make 225 | * sure that the layout is VISIBLE and that the empty view is GONE (if 226 | * it's not null). 227 | */ 228 | private void updateEmptyStatus(boolean empty) { 229 | if (empty) { 230 | if (mEmptyView != null) { 231 | mEmptyView.setVisibility(View.VISIBLE); 232 | setVisibility(View.GONE); 233 | } else { 234 | // If the caller just removed our empty view, make sure the list 235 | // view is visible 236 | setVisibility(View.VISIBLE); 237 | } 238 | } else { 239 | if (mEmptyView != null) 240 | mEmptyView.setVisibility(View.GONE); 241 | setVisibility(View.VISIBLE); 242 | } 243 | } 244 | 245 | private void setupChildren() { 246 | 247 | removeAllViews(); 248 | 249 | updateEmptyStatus((mAdapter == null) || mAdapter.isEmpty()); 250 | 251 | if (mAdapter == null) { 252 | return; 253 | } 254 | 255 | for (int i = 0; i < mAdapter.getCount(); i++) { 256 | View child = mAdapter.getView(i, null, this); 257 | if (mAreAllItemsSelectable || mAdapter.isEnabled(i)) { 258 | child.setOnClickListener(new InternalOnClickListener(i)); 259 | } 260 | addViewInLayout(child, -1, child.getLayoutParams(), true); 261 | } 262 | } 263 | 264 | /** 265 | * Internal OnClickListener that this view associate of each of its children 266 | * so that they can respond to OnItemClick listener's events. Avoid setting 267 | * an OnClickListener manually. If you need it you can wrap the child in a 268 | * simple {@link android.widget.FrameLayout}. 269 | */ 270 | private class InternalOnClickListener implements OnClickListener { 271 | 272 | int mPosition; 273 | 274 | public InternalOnClickListener(int position) { 275 | mPosition = position; 276 | } 277 | 278 | @Override 279 | public void onClick(View v) { 280 | if ((mOnItemClickListener != null) && (mAdapter != null)) { 281 | mOnItemClickListener.onItemClick(LinearListView.this, v, 282 | mPosition, mAdapter.getItemId(mPosition)); 283 | } 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /linearlistview/src/main/java/com/linearlistview/internal/IcsLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.linearlistview.internal; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.drawable.ColorDrawable; 7 | import android.graphics.drawable.Drawable; 8 | import android.os.Build; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | import android.widget.LinearLayout; 12 | 13 | /** 14 | * A simple extension of a regular linear layout that supports the divider API 15 | * of Android 4.0+. The dividers are added adjacent to the children by changing 16 | * their layout params. If you need to rely on the margins which fall in the 17 | * same orientation as the layout you should wrap the child in a simple 18 | * {@link android.widget.FrameLayout} so it can receive the margin. 19 | */ 20 | public class IcsLinearLayout extends LinearLayout { 21 | private static final int[] R_styleable_LinearLayout = new int[] { 22 | /* 0 */ android.R.attr.divider, 23 | /* 1 */ android.R.attr.measureWithLargestChild, 24 | /* 2 */ android.R.attr.showDividers, 25 | /* 3 */ android.R.attr.dividerPadding, 26 | }; 27 | private static final int LinearLayout_divider = 0; 28 | private static final int LinearLayout_measureWithLargestChild = 1; 29 | private static final int LinearLayout_showDividers = 2; 30 | private static final int LinearLayout_dividerPadding = 3; 31 | 32 | /** 33 | * Don't show any dividers. 34 | */ 35 | public static final int SHOW_DIVIDER_NONE = 0; 36 | /** 37 | * Show a divider at the beginning of the group. 38 | */ 39 | public static final int SHOW_DIVIDER_BEGINNING = 1; 40 | /** 41 | * Show dividers between each item in the group. 42 | */ 43 | public static final int SHOW_DIVIDER_MIDDLE = 2; 44 | /** 45 | * Show a divider at the end of the group. 46 | */ 47 | public static final int SHOW_DIVIDER_END = 4; 48 | 49 | 50 | private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; 51 | 52 | 53 | private Drawable mDivider; 54 | protected int mDividerWidth; 55 | protected int mDividerHeight; 56 | private int mShowDividers; 57 | private int mDividerPadding; 58 | private boolean mClipDivider; 59 | 60 | private boolean mUseLargestChild; 61 | 62 | public IcsLinearLayout(Context context, AttributeSet attrs) { 63 | super(context, attrs); 64 | 65 | TypedArray a = context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/R_styleable_LinearLayout); 66 | 67 | setDividerDrawable(a.getDrawable(/*com.android.internal.R.styleable.*/LinearLayout_divider)); 68 | mShowDividers = a.getInt(/*com.android.internal.R.styleable.*/LinearLayout_showDividers, SHOW_DIVIDER_NONE); 69 | mDividerPadding = a.getDimensionPixelSize(/*com.android.internal.R.styleable.*/LinearLayout_dividerPadding, 0); 70 | mUseLargestChild = a.getBoolean(/*com.android.internal.R.styleable.*/LinearLayout_measureWithLargestChild, false); 71 | 72 | a.recycle(); 73 | } 74 | 75 | /** 76 | * Set how dividers should be shown between items in this layout 77 | * 78 | * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING}, 79 | * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}, 80 | * or {@link #SHOW_DIVIDER_NONE} to show no dividers. 81 | */ 82 | public void setShowDividers(int showDividers) { 83 | if (showDividers != mShowDividers) { 84 | requestLayout(); 85 | invalidate(); //XXX This is required if you are toggling a divider off 86 | } 87 | mShowDividers = showDividers; 88 | } 89 | 90 | /** 91 | * @return A flag set indicating how dividers should be shown around items. 92 | * @see #setShowDividers(int) 93 | */ 94 | public int getShowDividers() { 95 | return mShowDividers; 96 | } 97 | 98 | /** 99 | * Set a drawable to be used as a divider between items. 100 | * @param divider Drawable that will divide each item. 101 | * @see #setShowDividers(int) 102 | */ 103 | public void setDividerDrawable(Drawable divider) { 104 | if (divider == mDivider) { 105 | return; 106 | } 107 | mDivider = divider; 108 | mClipDivider = divider instanceof ColorDrawable; 109 | if (divider != null) { 110 | mDividerWidth = divider.getIntrinsicWidth(); 111 | mDividerHeight = divider.getIntrinsicHeight(); 112 | } else { 113 | mDividerWidth = 0; 114 | mDividerHeight = 0; 115 | } 116 | setWillNotDraw(divider == null); 117 | requestLayout(); 118 | } 119 | 120 | /** 121 | * Set padding displayed on both ends of dividers. 122 | * 123 | * @param padding Padding value in pixels that will be applied to each end 124 | * 125 | * @see #setShowDividers(int) 126 | * @see #setDividerDrawable(android.graphics.drawable.Drawable) 127 | * @see #getDividerPadding() 128 | */ 129 | public void setDividerPadding(int padding) { 130 | mDividerPadding = padding; 131 | } 132 | 133 | /** 134 | * Get the padding size used to inset dividers in pixels 135 | * 136 | * @see #setShowDividers(int) 137 | * @see #setDividerDrawable(android.graphics.drawable.Drawable) 138 | * @see #setDividerPadding(int) 139 | */ 140 | public int getDividerPadding() { 141 | return mDividerPadding; 142 | } 143 | 144 | /** 145 | * Get the width of the current divider drawable. 146 | * 147 | * @hide Used internally by framework. 148 | */ 149 | public int getDividerWidth() { 150 | return mDividerWidth; 151 | } 152 | 153 | @Override 154 | protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { 155 | final int index = indexOfChild(child); 156 | final int orientation = getOrientation(); 157 | final LayoutParams params = (LayoutParams) child.getLayoutParams(); 158 | if (hasDividerBeforeChildAt(index)) { 159 | if (orientation == VERTICAL) { 160 | //Account for the divider by pushing everything up 161 | params.topMargin = mDividerHeight; 162 | } else { 163 | //Account for the divider by pushing everything left 164 | params.leftMargin = mDividerWidth; 165 | } 166 | } 167 | 168 | final int count = getChildCount(); 169 | if (index == count - 1) { 170 | if (hasDividerBeforeChildAt(count)) { 171 | if (orientation == VERTICAL) { 172 | params.bottomMargin = mDividerHeight; 173 | } else { 174 | params.rightMargin = mDividerWidth; 175 | } 176 | } 177 | } 178 | super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); 179 | } 180 | 181 | @Override 182 | protected void onDraw(Canvas canvas) { 183 | if (mDivider != null) { 184 | if (getOrientation() == VERTICAL) { 185 | drawDividersVertical(canvas); 186 | } else { 187 | drawDividersHorizontal(canvas); 188 | } 189 | } 190 | super.onDraw(canvas); 191 | } 192 | 193 | void drawDividersVertical(Canvas canvas) { 194 | final int count = getChildCount(); 195 | for (int i = 0; i < count; i++) { 196 | final View child = getChildAt(i); 197 | 198 | if (child != null && child.getVisibility() != GONE) { 199 | if (hasDividerBeforeChildAt(i)) { 200 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 201 | final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/; 202 | drawHorizontalDivider(canvas, top); 203 | } 204 | } 205 | } 206 | 207 | if (hasDividerBeforeChildAt(count)) { 208 | final View child = getChildAt(count - 1); 209 | int bottom = 0; 210 | if (child == null) { 211 | bottom = getHeight() - getPaddingBottom() - mDividerHeight; 212 | } else { 213 | //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 214 | bottom = child.getBottom()/* + lp.bottomMargin*/; 215 | } 216 | drawHorizontalDivider(canvas, bottom); 217 | } 218 | } 219 | 220 | void drawDividersHorizontal(Canvas canvas) { 221 | final int count = getChildCount(); 222 | for (int i = 0; i < count; i++) { 223 | final View child = getChildAt(i); 224 | 225 | if (child != null && child.getVisibility() != GONE) { 226 | if (hasDividerBeforeChildAt(i)) { 227 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 228 | final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/; 229 | drawVerticalDivider(canvas, left); 230 | } 231 | } 232 | } 233 | 234 | if (hasDividerBeforeChildAt(count)) { 235 | final View child = getChildAt(count - 1); 236 | int right = 0; 237 | if (child == null) { 238 | right = getWidth() - getPaddingRight() - mDividerWidth; 239 | } else { 240 | //final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 241 | right = child.getRight()/* + lp.rightMargin*/; 242 | } 243 | drawVerticalDivider(canvas, right); 244 | } 245 | } 246 | 247 | void drawHorizontalDivider(Canvas canvas, int top) { 248 | if(mClipDivider && !IS_HONEYCOMB) { 249 | canvas.save(); 250 | canvas.clipRect(getPaddingLeft() + mDividerPadding, top, 251 | getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); 252 | mDivider.draw(canvas); 253 | canvas.restore(); 254 | } else { 255 | mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, 256 | getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); 257 | mDivider.draw(canvas); 258 | } 259 | } 260 | 261 | void drawVerticalDivider(Canvas canvas, int left) { 262 | if(mClipDivider && !IS_HONEYCOMB) { 263 | canvas.save(); 264 | canvas.clipRect(left, getPaddingTop() + mDividerPadding, 265 | left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); 266 | mDivider.draw(canvas); 267 | canvas.restore(); 268 | } else { 269 | mDivider.setBounds(left, getPaddingTop() + mDividerPadding, 270 | left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); 271 | mDivider.draw(canvas); 272 | } 273 | } 274 | 275 | /** 276 | * Determines where to position dividers between children. 277 | * 278 | * @param childIndex Index of child to check for preceding divider 279 | * @return true if there should be a divider before the child at childIndex 280 | * @hide Pending API consideration. Currently only used internally by the system. 281 | */ 282 | protected boolean hasDividerBeforeChildAt(int childIndex) { 283 | if (childIndex == 0) { 284 | return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0; 285 | } else if (childIndex == getChildCount()) { 286 | return (mShowDividers & SHOW_DIVIDER_END) != 0; 287 | } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) { 288 | boolean hasVisibleViewBefore = false; 289 | for (int i = childIndex - 1; i >= 0; i--) { 290 | if (getChildAt(i).getVisibility() != GONE) { 291 | hasVisibleViewBefore = true; 292 | break; 293 | } 294 | } 295 | return hasVisibleViewBefore; 296 | } 297 | return false; 298 | } 299 | 300 | /** 301 | * When true, all children with a weight will be considered having 302 | * the minimum size of the largest child. If false, all children are 303 | * measured normally. 304 | * 305 | * @return True to measure children with a weight using the minimum 306 | * size of the largest child, false otherwise. 307 | * 308 | * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 309 | */ 310 | public boolean isMeasureWithLargestChildEnabled() { 311 | return mUseLargestChild; 312 | } 313 | 314 | /** 315 | * When set to true, all children with a weight will be considered having 316 | * the minimum size of the largest child. If false, all children are 317 | * measured normally. 318 | * 319 | * Disabled by default. 320 | * 321 | * @param enabled True to measure children with a weight using the 322 | * minimum size of the largest child, false otherwise. 323 | * 324 | * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 325 | */ 326 | public void setMeasureWithLargestChildEnabled(boolean enabled) { 327 | mUseLargestChild = enabled; 328 | } 329 | 330 | @Override 331 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 332 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 333 | 334 | if (mUseLargestChild) { 335 | final int orientation = getOrientation(); 336 | switch (orientation) { 337 | case HORIZONTAL: 338 | useLargestChildHorizontal(); 339 | break; 340 | 341 | case VERTICAL: 342 | useLargestChildVertical(); 343 | break; 344 | } 345 | } 346 | } 347 | 348 | private void useLargestChildHorizontal() { 349 | final int childCount = getChildCount(); 350 | 351 | // Find largest child width 352 | int largestChildWidth = 0; 353 | for (int i = 0; i < childCount; i++) { 354 | final View child = getChildAt(i); 355 | largestChildWidth = Math.max(child.getMeasuredWidth(), largestChildWidth); 356 | } 357 | 358 | int totalWidth = 0; 359 | // Re-measure childs 360 | for (int i = 0; i < childCount; i++) { 361 | final View child = getChildAt(i); 362 | 363 | if (child == null || child.getVisibility() == View.GONE) { 364 | continue; 365 | } 366 | 367 | final LayoutParams lp = 368 | (LayoutParams) child.getLayoutParams(); 369 | 370 | float childExtra = lp.weight; 371 | if (childExtra > 0) { 372 | child.measure( 373 | MeasureSpec.makeMeasureSpec(largestChildWidth, 374 | MeasureSpec.EXACTLY), 375 | MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), 376 | MeasureSpec.EXACTLY)); 377 | totalWidth += largestChildWidth; 378 | 379 | } else { 380 | totalWidth += child.getMeasuredWidth(); 381 | } 382 | 383 | totalWidth += lp.leftMargin + lp.rightMargin; 384 | } 385 | 386 | totalWidth += getPaddingLeft() + getPaddingRight(); 387 | setMeasuredDimension(totalWidth, getMeasuredHeight()); 388 | } 389 | 390 | private void useLargestChildVertical() { 391 | final int childCount = getChildCount(); 392 | 393 | // Find largest child width 394 | int largestChildHeight = 0; 395 | for (int i = 0; i < childCount; i++) { 396 | final View child = getChildAt(i); 397 | largestChildHeight = Math.max(child.getMeasuredHeight(), largestChildHeight); 398 | } 399 | 400 | int totalHeight = 0; 401 | // Re-measure childs 402 | for (int i = 0; i < childCount; i++) { 403 | final View child = getChildAt(i); 404 | 405 | if (child == null || child.getVisibility() == View.GONE) { 406 | continue; 407 | } 408 | 409 | final LayoutParams lp = 410 | (LayoutParams) child.getLayoutParams(); 411 | 412 | float childExtra = lp.weight; 413 | if (childExtra > 0) { 414 | child.measure( 415 | MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), 416 | MeasureSpec.EXACTLY), 417 | MeasureSpec.makeMeasureSpec(largestChildHeight, 418 | MeasureSpec.EXACTLY)); 419 | totalHeight += largestChildHeight; 420 | 421 | } else { 422 | totalHeight += child.getMeasuredHeight(); 423 | } 424 | 425 | totalHeight += lp.leftMargin + lp.rightMargin; 426 | } 427 | 428 | totalHeight += getPaddingLeft() + getPaddingRight(); 429 | setMeasuredDimension(getMeasuredWidth(), totalHeight); 430 | } 431 | } -------------------------------------------------------------------------------- /linearlistview/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:0.9.0' 7 | } 8 | } 9 | 10 | apply plugin: 'android' 11 | 12 | repositories { 13 | mavenCentral() 14 | 15 | maven { 16 | url "https://oss.sonatype.org/content/repositories/snapshots/" 17 | } 18 | } 19 | 20 | android { 21 | compileSdkVersion 18 22 | buildToolsVersion "19.0.0" 23 | 24 | defaultConfig { 25 | minSdkVersion 7 26 | targetSdkVersion 18 27 | } 28 | } 29 | 30 | dependencies { 31 | compile 'com.android.support:appcompat-v7:18.0.0' 32 | compile 'com.github.frankiesardo:linearlistview:0.9.0-SNAPSHOT@aar' 33 | } 34 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /sample/src/main/java/com/linearlistview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.linearlistview.sample; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.ColorDrawable; 6 | import android.os.Bundle; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.BaseAdapter; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | import android.widget.Toast; 15 | 16 | import com.linearlistview.LinearListView.OnItemClickListener; 17 | import com.linearlistview.LinearListView; 18 | 19 | public class MainActivity extends Activity { 20 | 21 | private BaseAdapter mAdapter = new BaseAdapter() { 22 | 23 | @Override 24 | public View getView(int position, View convertView, ViewGroup parent) { 25 | if (convertView == null) { 26 | convertView = getLayoutInflater().inflate(R.layout.list_item, parent, false); 27 | } 28 | ((TextView) convertView).setText("Position " + position); 29 | return convertView; 30 | } 31 | 32 | @Override 33 | public long getItemId(int position) { 34 | return position; 35 | } 36 | 37 | @Override 38 | public Object getItem(int position) { 39 | return position; 40 | } 41 | 42 | @Override 43 | public int getCount() { 44 | return mCount; 45 | } 46 | }; 47 | 48 | OnItemClickListener mListener = new OnItemClickListener() { 49 | 50 | @Override 51 | public void onItemClick(LinearListView parent, View view, int position, 52 | long id) { 53 | Toast.makeText(getApplicationContext(), 54 | "Tapped position " + position, Toast.LENGTH_SHORT).show(); 55 | 56 | } 57 | }; 58 | 59 | private int mCount = 50; 60 | 61 | @Override 62 | public void onCreate(Bundle savedInstanceState) { 63 | super.onCreate(savedInstanceState); 64 | setContentView(R.layout.activity_main); 65 | 66 | LinearListView horizontal = (LinearListView) findViewById(R.id.horizontal_list); 67 | LinearListView vertical = (LinearListView) findViewById(R.id.vertical_list); 68 | 69 | vertical.setDividerDrawable(new ColorDrawable(Color.CYAN)); 70 | vertical.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE 71 | | LinearLayout.SHOW_DIVIDER_BEGINNING); 72 | 73 | horizontal.setDividerDrawable(getResources().getDrawable(R.drawable.divider_vertical_holo_dark)); 74 | 75 | horizontal.setDividerThickness(getResources().getDimensionPixelSize(R.dimen.padding_medium)); 76 | vertical.setDividerThickness(getResources().getDimensionPixelSize(R.dimen.padding_small)); 77 | 78 | horizontal.setAdapter(mAdapter); 79 | vertical.setAdapter(mAdapter); 80 | 81 | horizontal.setOnItemClickListener(mListener); 82 | vertical.setOnItemClickListener(mListener); 83 | } 84 | 85 | @Override 86 | public boolean onCreateOptionsMenu(Menu menu) { 87 | getMenuInflater().inflate(R.menu.main, menu); 88 | return true; 89 | } 90 | 91 | @Override 92 | public boolean onOptionsItemSelected(MenuItem item) { 93 | if (item.getItemId() == R.id.menu_change) { 94 | mCount = (int) (Math.random() * 50); 95 | mAdapter.notifyDataSetChanged(); 96 | return true; 97 | } else 98 | return super.onOptionsItemSelected(item); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/divider_vertical_holo_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-hdpi/divider_vertical_holo_dark.9.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-hdpi/ic_action_search.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-ldpi/divider_vertical_holo_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-ldpi/divider_vertical_holo_dark.9.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/divider_vertical_holo_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-mdpi/divider_vertical_holo_dark.9.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-mdpi/ic_action_search.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/divider_vertical_holo_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-xhdpi/divider_vertical_holo_dark.9.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankiesardo/LinearListView/dd4490a997265922fb7989cc7787a663e4c9e634/sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 21 | 22 | 23 | 26 | 27 | 31 | 32 | 40 | 41 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 |

2 | 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 128dp 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 2dp 7 | 4dp 8 | 8dp 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LinearListView 5 | Hello world! 6 | 7 | 8 | Italy 9 | France 10 | US 11 | Spain 12 | UK 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':linearlistview' 2 | --------------------------------------------------------------------------------