├── .gitignore ├── README.md ├── build.gradle ├── demo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── chaychan │ │ └── expandablelinearlayout │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── chaychan │ │ │ └── expandablelinearlayout │ │ │ ├── EllCustomBottomDemoActivity.java │ │ │ ├── EllDefaultBottomDemoActivity.java │ │ │ ├── MainActivity.java │ │ │ └── bean │ │ │ └── ProductBean.java │ └── res │ │ ├── drawable │ │ └── shape_border.xml │ │ ├── layout │ │ ├── activity_expandable_linear_layout_choose.xml │ │ ├── activity_main.xml │ │ ├── item_product.xml │ │ ├── page_ell_custom_bottom_demo.xml │ │ └── page_ell_default_bottom_demo.xml │ │ ├── mipmap-hdpi │ │ ├── arrow_down_grey.png │ │ ├── ic_default.png │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── chaychan │ └── expandablelinearlayout │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── intro_img ├── ell_1.gif ├── ell_2.gif ├── ell_3.gif ├── ell_4.gif ├── ell_5.gif ├── ell_tip_text_demo.gif ├── meituan1.jpg └── meituan2.jpg ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── chaychan │ │ └── library │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── chaychan │ │ │ └── library │ │ │ ├── ExpandableLinearLayout.java │ │ │ └── UIUtils.java │ └── res │ │ ├── layout │ │ └── item_ell_bottom.xml │ │ ├── mipmap-hdpi │ │ └── arrow_down.png │ │ └── values │ │ ├── attr.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── chaychan │ └── library │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | /.idea 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ExpandableLinearLayout介绍 2 | ### 场景介绍 3 |   开发的过程中,有时我们需要使用到这样一个功能,在展示一些商品的时候,默认只显示前几个,例如先显示前三个,这样子不会一进入页面就被商品列表占据了大部分,可以先让用户可以看到页面的大概,当用户需要查看更多的商品时,点击“展开”,就可以看到被隐藏的商品,点击“收起”,则又回到一开始的状态,只显示前几个,其他的收起来了。就拿美团外卖的订单详情页的布局作为例子,请看以下图片: 4 | 5 | ![](./intro_img/meituan1.jpg) 6 | 7 | ![](./intro_img/meituan2.jpg) 8 | 9 |   订单详情页面一开始只显示购买的前三样菜,当点击“点击展开”时,则将购买的所有外卖都展示出来,当点击“点击收起”时,则将除了前三样菜以外的都隐藏起来。其实要完成这样的功能并不难,为了方便自己和大家以后的开发,我将其封装成一个控件,取名为ExpandableLinearLayout,下面开始介绍它如何使用以及源码解析。 10 | 11 | ## 使用方式 12 | ### 一、使用默认展开和收起的底部 13 | 在布局文件中,使用ExpandableLinearLayout,代码如下: 14 | 15 | 26 | 27 | 28 | 和LinearLayout的使用方法类似,如果是静态数据,可以在两个标签中间插入子条目布局的代码,也可以在java文件中使用代码动态插入。useDefaultBottom是指是否使用默认底部(默认为true,如果需要使用默认底部,可不写这个属性),如果是自定义的底部,则设置为false,下面会介绍自定义底部的用法,defaultItemCount="2",设置默认显示的个数为2,expandText为待展开时的文字提示,hideText为待收起时的文字提示。 29 | 30 | 在java文件中,根据id找到控件,动态往ExpandableLinearLayout中插入子条目并设置数据即可,代码如下: 31 | 32 | 33 | @Bind(R.id.ell_product) 34 | ExpandableLinearLayout ellProduct; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.page_ell_default_bottom_demo); 40 | ButterKnife.bind(this); 41 | 42 | ellProduct.removeAllViews();//清除所有的子View(避免重新刷新数据时重复添加) 43 | //添加数据 44 | for (int i = 0; i < 5; i++) { 45 | View view = View.inflate(this, R.layout.item_product, null); 46 | ProductBean productBean = new ProductBean(imgUrls[i], names[i], intros[i], "12.00"); 47 | ViewHolder viewHolder = new ViewHolder(view, productBean); 48 | viewHolder.refreshUI(); 49 | ellProduct.addItem(view);//添加子条目 50 | } 51 | } 52 | 53 | 54 | class ViewHolder { 55 | @Bind(R.id.iv_img) 56 | ImageView ivImg; 57 | @Bind(R.id.tv_name) 58 | TextView tvName; 59 | @Bind(R.id.tv_intro) 60 | TextView tvIntro; 61 | @Bind(R.id.tv_price) 62 | TextView tvPrice; 63 | 64 | ProductBean productBean; 65 | 66 | public ViewHolder(View view, ProductBean productBean) { 67 | ButterKnife.bind(this, view); 68 | this.productBean = productBean; 69 | } 70 | 71 | private void refreshUI() { 72 | Glide.with(EllDefaultBottomDemoActivity.this) 73 | .load(productBean.getImg()) 74 | .placeholder(R.mipmap.ic_default) 75 | .into(ivImg); 76 | tvName.setText(productBean.getName()); 77 | tvIntro.setText(productBean.getIntro()); 78 | tvPrice.setText("¥" + productBean.getPrice()); 79 | } 80 | } 81 | 82 | 效果如下: 83 | 84 | ![](./intro_img/ell_1.gif) 85 | 86 | #### 1.支持修改默认显示的个数 87 | 可以修改默认显示的个数,比如将其修改为3,即defaultItemCount="3" 88 | 89 | 效果如下: 90 | 91 | ![](./intro_img/ell_2.gif) 92 | 93 | #### 2.支持修改待展开和待收起状态下的文字提示 94 | 可以修改待展开状态和待收起状态下的文字提示,比如修改expandText="查看更多",hideText="收起更多" 95 | 96 | 效果如下: 97 | 98 | ![](./intro_img/ell_3.gif) 99 | 100 | 101 | #### 3.支持修改提示文字的大小、颜色 102 | 103 | 可以修改提示文字的大小和颜色,对应的属性分别是tipTextSize,tipTextColor。比如修改tipTextSize="16sp",tipTextColor="#ff7300" 104 | 105 | 效果如下: 106 | 107 | ![](./intro_img/ell_tip_text_demo.gif) 108 | 109 | 110 | #### 4.支持更换箭头的图标 111 | 可以修改箭头的图标,只需配置arrowDownImg属性,引用对应的图标,这里的箭头图标需要是向下的箭头,这样当展开和收起时,箭头会做相应的旋转动画。设置arrowDownImg="@mipmap/arrow\_down\_grey",修改为灰色的向下图标。 112 | 113 | 效果如下: 114 | 115 | ![](./intro_img/ell_4.gif) 116 | 117 | #### 5.设置条目点击事件 118 | 119 | ellProduct.setOnItemClickListener(new ExpandableLinearLayout.OnItemClickListener() { 120 | @Override 121 | public void onItemClick(View view, int position) { 122 | Toast.makeText(EllCustomBottomDemoActivity.this,names[position] , Toast.LENGTH_SHORT).show(); 123 | } 124 | }); 125 | 126 | ### 二、使用自定义底部 127 | 128 | 布局文件中,ExpandableLinearLayout配置useDefaultBottom="false",声明不使用默认底部。自己定义底部的布局。 129 | 130 | 131 | 137 | 138 | 143 | 144 | 145 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | java文件中,代码如下: 168 | 169 | @Override 170 | protected void onCreate(Bundle savedInstanceState) { 171 | super.onCreate(savedInstanceState); 172 | setContentView(R.layout.page_ell_custom_bottom_demo); 173 | ButterKnife.bind(this); 174 | 175 | ... //插入模拟数据的代码,和上面演示使用默认底部的代码一样 176 | 177 | //设置状态改变时的回调 178 | ellProduct.setOnStateChangeListener(new ExpandableLinearLayout.OnStateChangeListener() { 179 | @Override 180 | public void onStateChanged(boolean isExpanded) { 181 | doArrowAnim(isExpanded);//根据状态箭头旋转 182 | //根据状态更改文字提示 183 | if (isExpanded) { 184 | //展开 185 | tvTip.setText("点击收起"); 186 | } else { 187 | tvTip.setText("点击展开"); 188 | } 189 | } 190 | }); 191 | 192 | //为自定义的底部设置点击事件 193 | rlBottom.setOnClickListener(new View.OnClickListener() { 194 | @Override 195 | public void onClick(View v) { 196 | ellProduct.toggle(); 197 | } 198 | }); 199 | 200 | } 201 | 202 | // 箭头的动画 203 | private void doArrowAnim(boolean isExpand) { 204 | if (isExpand) { 205 | // 当前是展开,箭头由下变为上 206 | ObjectAnimator.ofFloat(ivArrow, "rotation", 0, 180).start(); 207 | } else { 208 | // 当前是收起,箭头由上变为下 209 | ObjectAnimator.ofFloat(ivArrow, "rotation", -180, 0).start(); 210 | } 211 | } 212 | 213 | 214 | 215 | 主要的代码是为ExpandableLinearLayout设置状态改变的回调,rlBottom为自定义底部的根布局RelativeLayout,为其设置点击事件,当点击的时候调用ExpandableLinearLayout的toggle()方法,当收到回调时,根据状态旋转箭头以及更改文字提示。 216 | 217 | 效果如下: 218 | 219 | ![](./intro_img/ell_5.gif) 220 | 221 | 到这里,ExpandableLinearLayout的使用就介绍完毕了,关于ExpandableLinearLayout的源码解析可以查看我的博客:[http://blog.csdn.net/Chay_Chan/article/details/72810770](http://blog.csdn.net/Chay_Chan/article/details/72810770) 222 | 223 | 224 | #### 导入方式 225 | 在项目根目录下的build.gradle中的allprojects{}中,添加jitpack仓库地址,如下: 226 | 227 | allprojects { 228 | repositories { 229 | jcenter() 230 | maven { url 'https://jitpack.io' }//添加jitpack仓库地址 231 | } 232 | } 233 | 234 | 打开app的module中的build.gradle,在dependencies{}中,添加依赖,如下: 235 | 236 | dependencies { 237 | compile 'com.github.chaychan:ExpandableLinearLayout:1.0.1' 238 | } 239 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "com.chaychan.expandablelinearlayout" 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.1' 29 | testCompile 'junit:junit:4.12' 30 | compile project(':library') 31 | compile 'com.github.bumptech.glide:glide:3.7.0' 32 | compile 'com.jakewharton:butterknife:7.0.0' 33 | } 34 | -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in G:\SDK\AndroidStudioSDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /demo/src/androidTest/java/com/chaychan/expandablelinearlayout/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.expandablelinearlayout; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.chaychan.expandablelinearlayout", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /demo/src/main/java/com/chaychan/expandablelinearlayout/EllCustomBottomDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.expandablelinearlayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.ImageView; 7 | import android.widget.RelativeLayout; 8 | import android.widget.TextView; 9 | import android.widget.Toast; 10 | 11 | import com.bumptech.glide.Glide; 12 | import com.chaychan.expandablelinearlayout.bean.ProductBean; 13 | import com.chaychan.library.ExpandableLinearLayout; 14 | import com.nineoldandroids.animation.ObjectAnimator; 15 | 16 | import butterknife.Bind; 17 | import butterknife.ButterKnife; 18 | 19 | /** 20 | * ExpandableLinearLayout使用自定义底部 21 | */ 22 | public class EllCustomBottomDemoActivity extends AppCompatActivity { 23 | 24 | @Bind(R.id.ell_product) 25 | ExpandableLinearLayout ellProduct; 26 | @Bind(R.id.tv_tip) 27 | TextView tvTip; 28 | @Bind(R.id.iv_arrow) 29 | ImageView ivArrow; 30 | @Bind(R.id.rl_bottom) 31 | RelativeLayout rlBottom; 32 | 33 | private String[] imgUrls = new String[]{ 34 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728066&di=e5669ad80a241da52b03301ee0ba2749&imgtype=jpg&er=1&src=http%3A%2F%2Fimg.taopic.com%2Fuploads%2Fallimg%2F121017%2F240425-12101H2202646.jpg", 35 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728145&di=c2ece04e1445eaf91fe3f3bf12ad1080&imgtype=jpg&er=1&src=http%3A%2F%2Fimg1.qunarzz.com%2Ftravel%2Fd6%2F1610%2F33%2F21ce9c91e70ab7b5.jpg_r_720x480x95_b2bcd2c5.jpg", 36 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728182&di=1e06ea8b74863155b9d52736093beda8&imgtype=jpg&er=1&src=http%3A%2F%2Fe.hiphotos.baidu.com%2Fbainuo%2Fcrop%3D0%2C0%2C470%2C285%3Bw%3D470%3Bq%3D79%2Fsign%3Da8aa38e3b73533fae1f9c96e95e3d12f%2F6c224f4a20a44623b885148f9e22720e0df3d794.jpg", 37 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496133522433&di=1132cb36274a205f8ce30e21f47a37ee&imgtype=0&src=http%3A%2F%2Fi3.s2.dpfile.com%2Fpc%2Fb68a2a4316ae56373e83ce65ad7dfada%2528249x249%2529%2Fthumb.jpg", 38 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728305&di=444bfe10c434c09043855e7a6a7f8ace&imgtype=jpg&er=1&src=http%3A%2F%2Fe.hiphotos.baidu.com%2Fbainuo%2Fcrop%3D0%2C0%2C470%2C285%3Bw%3D470%3Bq%3D99%2Fsign%3D65498f21374e251ff6b8beb89ab6e527%2F0df3d7ca7bcb0a46d662a6226c63f6246b60af6c.jpg" 39 | }; 40 | 41 | private String[] names = new String[]{ 42 | "炒河粉", 43 | "炒米粉", 44 | "隆江猪脚饭", 45 | "烧鸭饭", 46 | "叉烧饭" 47 | }; 48 | 49 | private String[] intros = new String[]{ 50 | "好吃又不腻", 51 | "精选上等米粉,绝对好吃", 52 | "隆江猪脚饭,肥而不腻,入口香爽,深受广东人民的喜爱", 53 | "简单而美味,充满烧腊香味", 54 | "色香味俱全" 55 | }; 56 | 57 | @Override 58 | protected void onCreate(Bundle savedInstanceState) { 59 | super.onCreate(savedInstanceState); 60 | setContentView(R.layout.page_ell_custom_bottom_demo); 61 | ButterKnife.bind(this); 62 | 63 | 64 | ellProduct.removeAllViews();//清除所有的子View(避免重新刷新数据时重复添加) 65 | //添加数据 66 | for (int i = 0; i < 4; i++) { 67 | View view = View.inflate(this, R.layout.item_product, null); 68 | ProductBean productBean = new ProductBean(imgUrls[i], names[i], intros[i], "12.00"); 69 | ViewHolder viewHolder = new ViewHolder(view, productBean); 70 | viewHolder.refreshUI(); 71 | ellProduct.addItem(view);//添加子条目 72 | } 73 | 74 | ellProduct.setOnStateChangeListener(new ExpandableLinearLayout.OnStateChangeListener() { 75 | @Override 76 | public void onStateChanged(boolean isExpanded) { 77 | doArrowAnim(isExpanded);//根据状态箭头旋转 78 | //根据状态更改文字提示 79 | if (isExpanded) { 80 | //展开 81 | tvTip.setText("点击收起"); 82 | } else { 83 | tvTip.setText("点击展开"); 84 | } 85 | } 86 | }); 87 | 88 | rlBottom.setOnClickListener(new View.OnClickListener() { 89 | @Override 90 | public void onClick(View v) { 91 | ellProduct.toggle(); 92 | } 93 | }); 94 | 95 | ellProduct.setOnItemClickListener(new ExpandableLinearLayout.OnItemClickListener() { 96 | @Override 97 | public void onItemClick(View view, int position) { 98 | Toast.makeText(EllCustomBottomDemoActivity.this,names[position] , Toast.LENGTH_SHORT).show(); 99 | } 100 | }); 101 | } 102 | 103 | // 箭头的动画 104 | private void doArrowAnim(boolean isExpand) { 105 | if (isExpand) { 106 | // 当前是展开,箭头由下变为上 107 | ObjectAnimator.ofFloat(ivArrow, "rotation", 0, 180).start(); 108 | } else { 109 | // 当前是收起,箭头由上变为下 110 | ObjectAnimator.ofFloat(ivArrow, "rotation", -180, 0).start(); 111 | } 112 | } 113 | 114 | class ViewHolder { 115 | @Bind(R.id.iv_img) 116 | ImageView ivImg; 117 | @Bind(R.id.tv_name) 118 | TextView tvName; 119 | @Bind(R.id.tv_intro) 120 | TextView tvIntro; 121 | @Bind(R.id.tv_price) 122 | TextView tvPrice; 123 | 124 | ProductBean productBean; 125 | 126 | public ViewHolder(View view, ProductBean productBean) { 127 | ButterKnife.bind(this, view); 128 | this.productBean = productBean; 129 | } 130 | 131 | private void refreshUI() { 132 | Glide.with(EllCustomBottomDemoActivity.this) 133 | .load(productBean.getImg()) 134 | .placeholder(R.mipmap.ic_default) 135 | .into(ivImg); 136 | tvName.setText(productBean.getName()); 137 | tvIntro.setText(productBean.getIntro()); 138 | tvPrice.setText("¥" + productBean.getPrice()); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /demo/src/main/java/com/chaychan/expandablelinearlayout/EllDefaultBottomDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.expandablelinearlayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | import android.widget.Toast; 9 | 10 | import com.bumptech.glide.Glide; 11 | import com.chaychan.expandablelinearlayout.bean.ProductBean; 12 | import com.chaychan.library.ExpandableLinearLayout; 13 | 14 | import butterknife.Bind; 15 | import butterknife.ButterKnife; 16 | 17 | /** 18 | * ExpandableLinearLayout使用默认底部 19 | */ 20 | public class EllDefaultBottomDemoActivity extends AppCompatActivity { 21 | 22 | @Bind(R.id.ell_product) 23 | ExpandableLinearLayout ellProduct; 24 | 25 | private String[] imgUrls = new String[]{ 26 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728066&di=e5669ad80a241da52b03301ee0ba2749&imgtype=jpg&er=1&src=http%3A%2F%2Fimg.taopic.com%2Fuploads%2Fallimg%2F121017%2F240425-12101H2202646.jpg", 27 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728145&di=c2ece04e1445eaf91fe3f3bf12ad1080&imgtype=jpg&er=1&src=http%3A%2F%2Fimg1.qunarzz.com%2Ftravel%2Fd6%2F1610%2F33%2F21ce9c91e70ab7b5.jpg_r_720x480x95_b2bcd2c5.jpg", 28 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728182&di=1e06ea8b74863155b9d52736093beda8&imgtype=jpg&er=1&src=http%3A%2F%2Fe.hiphotos.baidu.com%2Fbainuo%2Fcrop%3D0%2C0%2C470%2C285%3Bw%3D470%3Bq%3D79%2Fsign%3Da8aa38e3b73533fae1f9c96e95e3d12f%2F6c224f4a20a44623b885148f9e22720e0df3d794.jpg", 29 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496133522433&di=1132cb36274a205f8ce30e21f47a37ee&imgtype=0&src=http%3A%2F%2Fi3.s2.dpfile.com%2Fpc%2Fb68a2a4316ae56373e83ce65ad7dfada%2528249x249%2529%2Fthumb.jpg", 30 | "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496728305&di=444bfe10c434c09043855e7a6a7f8ace&imgtype=jpg&er=1&src=http%3A%2F%2Fe.hiphotos.baidu.com%2Fbainuo%2Fcrop%3D0%2C0%2C470%2C285%3Bw%3D470%3Bq%3D99%2Fsign%3D65498f21374e251ff6b8beb89ab6e527%2F0df3d7ca7bcb0a46d662a6226c63f6246b60af6c.jpg" 31 | }; 32 | 33 | private String[] names = new String[]{ 34 | "炒河粉", 35 | "炒米粉", 36 | "隆江猪脚饭", 37 | "烧鸭饭", 38 | "叉烧饭" 39 | }; 40 | 41 | private String[] intros = new String[]{ 42 | "好吃又不腻", 43 | "精选上等米粉,绝对好吃", 44 | "隆江猪脚饭,肥而不腻,入口香爽,深受广东人民的喜爱", 45 | "简单而美味,充满烧腊香味", 46 | "色香味俱全" 47 | }; 48 | 49 | @Override 50 | protected void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | setContentView(R.layout.page_ell_default_bottom_demo); 53 | ButterKnife.bind(this); 54 | 55 | ellProduct.removeAllViews();//清除所有的子View(避免重新刷新数据时重复添加) 56 | //添加数据 57 | for (int i = 0; i < 5; i++) { 58 | View view = View.inflate(this, R.layout.item_product, null); 59 | ProductBean productBean = new ProductBean(imgUrls[i], names[i], intros[i], "12.00"); 60 | ViewHolder viewHolder = new ViewHolder(view, productBean); 61 | viewHolder.refreshUI(); 62 | ellProduct.addItem(view);//添加子条目 63 | } 64 | 65 | ellProduct.setOnItemClickListener(new ExpandableLinearLayout.OnItemClickListener() { 66 | @Override 67 | public void onItemClick(View view, int position) { 68 | Toast.makeText(EllDefaultBottomDemoActivity.this,names[position] , Toast.LENGTH_SHORT).show(); 69 | } 70 | }); 71 | } 72 | 73 | class ViewHolder { 74 | @Bind(R.id.iv_img) 75 | ImageView ivImg; 76 | @Bind(R.id.tv_name) 77 | TextView tvName; 78 | @Bind(R.id.tv_intro) 79 | TextView tvIntro; 80 | @Bind(R.id.tv_price) 81 | TextView tvPrice; 82 | 83 | ProductBean productBean; 84 | 85 | public ViewHolder(View view, ProductBean productBean) { 86 | ButterKnife.bind(this, view); 87 | this.productBean = productBean; 88 | } 89 | 90 | private void refreshUI() { 91 | Glide.with(EllDefaultBottomDemoActivity.this) 92 | .load(productBean.getImg()) 93 | .placeholder(R.mipmap.ic_default) 94 | .into(ivImg); 95 | tvName.setText(productBean.getName()); 96 | tvIntro.setText(productBean.getIntro()); 97 | tvPrice.setText("¥" + productBean.getPrice()); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /demo/src/main/java/com/chaychan/expandablelinearlayout/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.expandablelinearlayout; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_main); 14 | } 15 | 16 | //使用默认底部 17 | public void useDefaultBottom(View view){ 18 | Intent intent = new Intent(this, EllDefaultBottomDemoActivity.class); 19 | startActivity(intent); 20 | } 21 | 22 | //使用自定义底部 23 | public void useCustomBottom(View view){ 24 | Intent intent = new Intent(this, EllCustomBottomDemoActivity.class); 25 | startActivity(intent); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/src/main/java/com/chaychan/expandablelinearlayout/bean/ProductBean.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.expandablelinearlayout.bean; 2 | 3 | /** 4 | * @author ChayChan 5 | * @description: 商品的bean类 6 | * @date 2017/6/25 14:06 7 | */ 8 | public class ProductBean { 9 | private String img; 10 | private String name; 11 | private String intro; 12 | private String price; 13 | 14 | public ProductBean(String img, String name, String intro, String price) { 15 | this.img = img; 16 | this.name = name; 17 | this.intro = intro; 18 | this.price = price; 19 | } 20 | 21 | public String getImg() { 22 | return img; 23 | } 24 | 25 | public void setImg(String img) { 26 | this.img = img; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public String getIntro() { 38 | return intro; 39 | } 40 | 41 | public void setIntro(String intro) { 42 | this.intro = intro; 43 | } 44 | 45 | public String getPrice() { 46 | return price; 47 | } 48 | 49 | public void setPrice(String price) { 50 | this.price = price; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/shape_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_expandable_linear_layout_choose.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |