package com.loaderman.expandablelinearlayout;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* 可以展开的LinearLayout
*/
public class ExpandableLinearLayout extends LinearLayout implements View.OnClickListener { private static final String TAG = ExpandableLinearLayout.class.getSimpleName(); private TextView tvTip;
private ImageView ivArrow; private boolean isExpand = false;//是否是展开状态,默认是隐藏 private int defaultItemCount;//一开始展示的条目数
private String expandText;//待展开显示的文字
private String hideText;//待隐藏显示的文字
private boolean useDefaultBottom;//是否使用默认的底部,默认为true使用默认的底部
private boolean hasBottom;//是否已经有底部,默认为false,没有
private View bottomView;
private float fontSize;
private int textColor;
private int arrowResId; public ExpandableLinearLayout(Context context) {
this(context, null);
} public ExpandableLinearLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public ExpandableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandableLinearLayout);
defaultItemCount = ta.getInt(R.styleable.ExpandableLinearLayout_defaultItemCount, 2);
expandText = ta.getString(R.styleable.ExpandableLinearLayout_expandText);
hideText = ta.getString(R.styleable.ExpandableLinearLayout_hideText);
fontSize = ta.getDimension(R.styleable.ExpandableLinearLayout_tipTextSize, UIUtils.sp2px(context, 14));
textColor = ta.getColor(R.styleable.ExpandableLinearLayout_tipTextColor, Color.parseColor("#666666"));
arrowResId = ta.getResourceId(R.styleable.ExpandableLinearLayout_arrowDownImg, R.mipmap.arrow_down);
useDefaultBottom = ta.getBoolean(R.styleable.ExpandableLinearLayout_useDefaultBottom, true);
ta.recycle(); setOrientation(VERTICAL);
} /**
* 渲染完成时初始化默认底部view
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
findViews();
} /**
* 初始化底部view
*/
private void findViews() {
bottomView = View.inflate(getContext(), R.layout.item_ell_bottom, null);
ivArrow = (ImageView) bottomView.findViewById(R.id.iv_arrow); tvTip = (TextView) bottomView.findViewById(R.id.tv_tip);
tvTip.getPaint().setTextSize(fontSize);
tvTip.setTextColor(textColor);
ivArrow.setImageResource(arrowResId); bottomView.setOnClickListener(this);
} public void addItem(View view) {
int childCount = getChildCount();
if (!useDefaultBottom){
//如果不使用默认底部
addView(view);
if (childCount > defaultItemCount){
hide();
}
return;
} //使用默认底部
if (!hasBottom) {
//如果还没有底部
addView(view);
} else {
addView(view, childCount - 2);//插在底部之前
}
refreshUI(view);
} @Override
public void setOrientation(int orientation) {
if (LinearLayout.HORIZONTAL == orientation) {
throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation.");
}
super.setOrientation(orientation);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childCount = getChildCount();
Log.i(TAG, "childCount: " + childCount);
justToAddBottom(childCount);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} /**
* 判断是否要添加底部
* @param childCount
*/
private void justToAddBottom(int childCount) {
if (childCount > defaultItemCount) {
if (useDefaultBottom && !hasBottom) {
//要使用默认底部,并且还没有底部
addView(bottomView);//添加底部
hide();
hasBottom = true;
}
}
} /**
* 刷新UI
*
* @param view
*/
private void refreshUI(View view) {
int childCount = getChildCount();
if (childCount > defaultItemCount) {
if (childCount - defaultItemCount == 1) {
//刚超过默认,判断是否要添加底部
justToAddBottom(childCount);
}
view.setVisibility(GONE);//大于默认数目的先隐藏
}
} /**
* 展开
*/
private void expand() {
for (int i = defaultItemCount; i < getChildCount(); i++) {
//从默认显示条目位置以下的都显示出来
View view = getChildAt(i);
view.setVisibility(VISIBLE);
}
} /**
* 收起
*/
private void hide() {
int endIndex = useDefaultBottom ? getChildCount() - 1 : getChildCount();//如果是使用默认底部,则结束的下标是到底部之前,否则则全部子条目都隐藏
for (int i = defaultItemCount; i < endIndex; i++) {
//从默认显示条目位置以下的都隐藏
View view = getChildAt(i);
view.setVisibility(GONE);
}
} // 箭头的动画
private void doArrowAnim() {
if (isExpand) {
// 当前是展开,将执行收起,箭头由上变为下
ObjectAnimator.ofFloat(ivArrow, "rotation", -180, 0).start();
} else {
// 当前是收起,将执行展开,箭头由下变为上
ObjectAnimator.ofFloat(ivArrow, "rotation", 0, 180).start();
}
} @Override
public void onClick(View v) {
toggle();
} public void toggle() {
if (isExpand) {
hide();
tvTip.setText(expandText);
} else {
expand();
tvTip.setText(hideText);
}
doArrowAnim();
isExpand = !isExpand; //回调
if (mListener != null){
mListener.onStateChanged(isExpand);
}
} private OnStateChangeListener mListener; /**
* 定义状态改变接口
*/
public interface OnStateChangeListener {
void onStateChanged(boolean isExpanded);
} public void setOnStateChangeListener(OnStateChangeListener mListener) {
this.mListener = mListener;
}
}

UIUtil.java

package com.loaderman.expandablelinearlayout;

import android.content.Context;

public class UIUtils {
/**
* dip-->px
*/
public static int dip2Px(Context context,int dip) {
// px/dip = density;
// density = dpi/160
// 320*480 density = 1 1px = 1dp
// 1280*720 density = 2 2px = 1dp float density = context.getResources().getDisplayMetrics().density;
int px = (int) (dip * density + 0.5f);
return px;
} /**
* 将sp值转换为px值,保证文字大小不变
*
* @param spValue
* @return
*/
public static int sp2px(Context context,float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}

attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ExpandableLinearLayout">
<!--默认显示的条目数-->
<attr name="defaultItemCount" format="integer" />
<!--提示文字的大小-->
<attr name="tipTextSize" format="dimension" />
<!--字体颜色-->
<attr name="tipTextColor" format="color"/>
<!--待展开的文字提示-->
<attr name="expandText" format="string" />
<!--待收起时的文字提示-->
<attr name="hideText" format="string" />
<!--向下的箭头的图标-->
<attr name="arrowDownImg" format="reference" />
<!--是否使用默认的底部-->
<attr name="useDefaultBottom" format="boolean" />
</declare-styleable>
</resources>

item_ell_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10dp"
> <TextView
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="查看更多"
/> <ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@id/tv_tip"
/> </RelativeLayout>

ProductBean.java

package com.loaderman.expandablelinearlayout;

public class ProductBean {
private String img;
private String name;
private String intro;
private String price; public ProductBean(String img, String name, String intro, String price) {
this.img = img;
this.name = name;
this.intro = intro;
this.price = price;
} public String getImg() {
return img;
} public void setImg(String img) {
this.img = img;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getIntro() {
return intro;
} public void setIntro(String intro) {
this.intro = intro;
} public String getPrice() {
return price;
} public void setPrice(String price) {
this.price = price;
}
}

MainActivity.java

package com.loaderman.expandablelinearlayout;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView; import com.bumptech.glide.Glide; import butterknife.Bind;
import butterknife.ButterKnife; public class MainActivity extends AppCompatActivity {
@Bind(R.id.ell_product)
ExpandableLinearLayout ellProduct; private String[] imgUrls = new String[]{
"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",
"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",
"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",
"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",
"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"
}; private String[] names = new String[]{
"炒河粉",
"炒米粉",
"隆江猪脚饭",
"烧鸭饭",
"叉烧饭"
}; private String[] intros = new String[]{
"好吃又不腻",
"精选上等米粉,绝对好吃",
"隆江猪脚饭,肥而不腻,入口香爽,深受广东人民的喜爱",
"简单而美味,充满烧腊香味",
"色香味俱全"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this); ellProduct.removeAllViews();//清除所有的子View(避免重新刷新数据时重复添加)
//添加数据
for (int i = 0; i < 5; i++) {
View view = View.inflate(this, R.layout.item_product, null);
ProductBean productBean = new ProductBean(imgUrls[i], names[i], intros[i], "12.00");
ViewHolder viewHolder = new ViewHolder(view, productBean);
viewHolder.refreshUI();
ellProduct.addItem(view);//添加子条目
}
}class ViewHolder {
@Bind(R.id.iv_img)
ImageView ivImg;
@Bind(R.id.tv_name)
TextView tvName;
@Bind(R.id.tv_intro)
TextView tvIntro;
@Bind(R.id.tv_price)
TextView tvPrice; ProductBean productBean; public ViewHolder(View view, ProductBean productBean) {
ButterKnife.bind(this, view);
this.productBean = productBean;
} private void refreshUI() {
Glide.with(MainActivity.this)
.load(productBean.getImg())
.placeholder(R.mipmap.ic_launcher)
.into(ivImg);
tvName.setText(productBean.getName());
tvIntro.setText(productBean.getIntro());
tvPrice.setText("¥" + productBean.getPrice());
}
} }

item_product.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
> <ImageView
android:id="@+id/iv_img"
android:layout_width="100dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher"
/> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="vertical"
> <TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="隆江猪脚饭"
/> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:orientation="horizontal"
> <TextView
android:id="@+id/tv_intro"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="特别好吃哦,带回家啊时间还是健康的贺卡上空间的挥洒健康的贺卡姐啊上课黑色的健康哈空间"
android:textColor="#9f9f9f"
/> <TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:drawablePadding="5dp"
android:text="12.00"
/> </LinearLayout> </LinearLayout> </LinearLayout> <View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#D1D1D1"/> </LinearLayout>

最后添加依赖:

     compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.jakewharton:butterknife:7.0.0'

添加网络权限:

<uses-permission android:name="android.permission.INTERNET"/>

效果图:


学习来源:http://blog.csdn.net/chay_chan/article/details/72810770


可以展开和收起的的LinearLayout的更多相关文章

  1. 【Android】键盘的展开和收起

    键盘的展开和收起主要使用到类InputMethodManager:http://developer.android.com/reference/android/view/inputmethod/Inp ...

  2. js 点击展开、收起

    //点击展开.收起 window.onload=function(){ var current=document.getElementsByTagName('li')[0]; document.bod ...

  3. UITableView多层展开与收起

    规则要求: tableview 有多层,类似于xcode文件目录的层级关系,每一个最开始展示的层姑且称之为根目录吧,并且,每个根目录下的层数不定. 与文件目录类似,每个目录下可以有不同层级的目录同时展 ...

  4. 长图的展开与收起(Android)

    前言: 在app的文章中,经常会夹杂着一些特别长的长图.在阅读的时候需要滑动很久才能看图片下方的文字,因此对于长图只展示图片上面一部分,并且可以展开这个功能是很重要的. 效果: 基本思路: 利用sca ...

  5. jQuery实现画面的展开、收起和停止

    主要用到动画效果中的三个操作 ("#id").slideDown(3000): // 后面的数字表示效果的时长 ("#id").stop(); ("# ...

  6. div展开与收起(鼠标点击)

    效果图: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  7. Silverlight自定义控件系列 – TreeView (3) 添加展开和收起事件

    由于Writer嫌我文章过长,只能把上篇拆开两半了.以下是接着上篇的. 准备工作做完了,现在就要完成点击事件. 定义Expander和单击事件: 1: /// <summary> 2: / ...

  8. Vue:列表展开和收起(超过一定行数时显示‘查看更多’按钮)

    前言:前端小白记录的一些小功能~ 公司开发中的小程序中有做任务签到的功能,这就涉及到了任务列表以及对任务列表的展开和收起功能,好了可以开始了,说多了就烦了 1.首先是css样式,因为设计稿上是超过两行 ...

  9. 巧妙利用before和after伪类实现文字的展开和收起

    需求:一段文字,当收起的时候,显示4行,并且多余4行的部分用省略号表示,关键是在省略号前面留有空白部分来放一些图标等东西:展开的时候,全部显示. 例如下面的示例图: 收起的时候: 展开的时候: 在不用 ...

随机推荐

  1. spring 在web.xml 里面如何使用多个xml配置文件

    1, 在web.xml中定义 contextConfigLocation参数.spring会使用这个参数加载.所有逗号分割的xml.如果没有这个参数,spring默认加载web-inf/applica ...

  2. zabbix分布式部署和主机自动发现

    1.分布式部署原理 1.1Zabbix分布式部署的原理 传统的部署架构,是server直接监控所有的主机,全部主机的数据都是有server自己来采集和处理,server端的压力比较大,当监控主机数量很 ...

  3. c++ 初学者的画图库EasyX

    EasyX 什么是easyx? EasyX 是针对 C++ 的图形库,可以帮助 C++语言初学者快速上手图形和游戏编程.其实就是c++的一个图形库让初学者不用只在控制台输出代码,而是在图形界面进行开发 ...

  4. maven 学习之路之二(1)

    上次我简单讲了maven的安装和构建生命周期. 这一篇博客我将用实际项目来分享下maven整个构建生命周期的具体使用: 这次我将用maven做一个自己写程序的一个模版程序. 自己实现一个简单的页面登录 ...

  5. 01-jar包操作---idea打jar包

    文章:idea打包java可执行jar包 maven项目的话,使用maven命令,直接build就可以打jar包.

  6. const与constexpr

    关于const型数据,谭浩强老爷子这么总结道: Time const t; //t是常对象,其值在任何情况下都不能改变 void Time::fun()const; //fun是Time类中的常成员函 ...

  7. Gym - 101915D Largest Group 最大团

    给你一个二分图 问你最大团为多大 解一:状压DP 解二:二分图最大匹配 二分图的最大团=补图的最大独立集 二分图最大独立集=二分图定点个数-最大匹配 //Hungary #include<bit ...

  8. 10个不为人知的 Python 冷知识

    转载: 1. 省略号也是对象 ...这是省略号,在Python中,一切皆对象.它也不例外. 在 Python 中,它叫做 Ellipsis . 在 Python 3 中你可以直接写…来得到这玩意. 而 ...

  9. Protobuffer教程

    目录 什么是protobuffer? protobuffer是如何工作的? 为什么不用xml? 1.什么是protobuffer? protobuffer是一种灵活,高效,自动化的机制,用于序列化结构 ...

  10. 什么?studio3T试用期到了,还没有破解的办法?试制基于python的mongodb CRUD平台

    首先,安装python支持的mongodb库pip install pymongo from pymongo import MongoClient client = MongoClient('loca ...