提交 4958460f 编写于 作者: C chaychan

增加ExpandableLinearLayout控件,用于收起和查看更多

上级 4e33699a
......@@ -3,9 +3,11 @@ package com.chaychan.powerfulviewlibrary;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.chaychan.viewlib.ExpandableLinearLayout;
import com.chaychan.viewlib.PowerfulEditText;
public class MainActivity extends AppCompatActivity {
......@@ -25,5 +27,17 @@ public class MainActivity extends AppCompatActivity {
}
}
});
View view1 = View.inflate(this, R.layout.item_expandable_linear_layout, null);
View view2 = View.inflate(this, R.layout.item_expandable_linear_layout, null);
View view3 = View.inflate(this, R.layout.item_expandable_linear_layout, null);
View view4 = View.inflate(this, R.layout.item_expandable_linear_layout, null);
ExpandableLinearLayout expandableLinearLayout = (ExpandableLinearLayout) findViewById(R.id.ell);
expandableLinearLayout.addItem(view1);
expandableLinearLayout.addItem(view2);
expandableLinearLayout.addItem(view3);
expandableLinearLayout.addItem(view4);
}
}
......@@ -15,6 +15,17 @@
android:drawableRight="@mipmap/search"
/>
<com.chaychan.viewlib.BankCardNumEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.chaychan.viewlib.ExpandableLinearLayout
android:id="@+id/ell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:defaultItemCount="2"
app:expandText="收起"
app:hideText="查看更多"
/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/eye_close"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="据阿克苏基督教"
/>
</LinearLayout>
\ No newline at end of file
......@@ -6,7 +6,6 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
......
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
## Project-wide Gradle settings.
#
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
\ No newline at end of file
# org.gradle.parallel=true
#Fri Mar 31 10:17:35 CST 2017
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=9666
......@@ -22,4 +22,5 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.nineoldandroids:library:2.4.0'
}
package com.chaychan.viewlib;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
/**
* 银行卡号输入框
*
* @author Administrator
*
*/
public class BankCardNumEditText extends EditText {
public BankCardNumEditText(Context context) {
super(context);
this.addTextChangedListener(new NumberTextWatcher(this,' '));
}
public BankCardNumEditText(Context context, AttributeSet attrs) {
super(context, attrs);
this.addTextChangedListener(new NumberTextWatcher(this,' '));
}
/**
* 获取真实的text(去掉空格)
*
* @return
*/
public String getTextWithoutSpace() {
String text = super.getText().toString();
if (android.text.TextUtils.isEmpty(text)) {
return "";
} else {
return text.replace(" ", "");
}
}
}
\ No newline at end of file
package com.chaychan.viewlib;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.ValueAnimator;
/**
* 可以展开的LinearLayout
*/
public class ExpandableLinearLayout extends FrameLayout implements View.OnClickListener {
private LinearLayout llContainer;
private TextView tvTip;
private boolean isExpand = false;//是否是展开状态,默认是隐藏
private int defaultItemCount;//一开始展示的条目数
private String expandText;//展开时显示的文字
private String hideText;//隐藏时显示的文字
private ImageView ivArrow;
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, 0);
expandText = ta.getString(R.styleable.ExpandableLinearLayout_expandText);
hideText = ta.getString(R.styleable.ExpandableLinearLayout_hideText);
float fontSize = ta.getDimension(R.styleable.ExpandableLinearLayout_tipTextSize,UIUtils.sp2px(context,14));
int textColor = ta.getColor(R.styleable.ExpandableLinearLayout_tipTextColor, Color.parseColor("#666666"));
ta.recycle();
View rootView = View.inflate(context, R.layout.expandable_linearlayout, null);
llContainer = (LinearLayout) rootView.findViewById(R.id.ll_container);
RelativeLayout rlBottom = (RelativeLayout) rootView.findViewById(R.id.rl_bottom);
ivArrow = (ImageView) rootView.findViewById(R.id.iv_arrow);
tvTip = (TextView) rootView.findViewById(R.id.tv_tip);
tvTip.getPaint().setTextSize(fontSize);
tvTip.setTextColor(textColor);
rlBottom.setOnClickListener(this);
addView(rootView);
}
public void addItem(View view) {
llContainer.addView(view);
refreshUI();
}
/**
* 刷新UI
*/
private void refreshUI() {
int childCount = llContainer.getChildCount();
tvTip.setVisibility(childCount > defaultItemCount ? VISIBLE : GONE);//控制隐藏
if (childCount > defaultItemCount) {
hide(false);
}
}
/**
* 展开
*/
private void expand() {
int childCount = llContainer.getChildCount();
//如果含有条目数大于默认显示的条目数,则可以展开,改变linearLayout的高度
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) llContainer.getLayoutParams();
int height = 0;
for (int i = 0; i < childCount; i++) {
View view = llContainer.getChildAt(i);
int viewWidth = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int viewHeight = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(viewWidth, viewHeight);
height += view.getMeasuredHeight();
}
doAnimation(params.height,height);//执行动画
}
/**
* 收起
*/
private void hide(boolean withAnimation) {
//改变linearLayout的高度为defaultItemCount个条目的高度
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) llContainer.getLayoutParams();
int height = 0;
for (int i = 0; i < defaultItemCount; i++) {
View view = llContainer.getChildAt(i);
int viewWidth = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int viewHeight = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(viewWidth, viewHeight);
height += view.getMeasuredHeight();
}
if (withAnimation){
doAnimation(params.height, height);//执行动画
}else{
params.height = height;
llContainer.setLayoutParams(params);
}
}
private void doAnimation(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);// 由初始值向结束值变化
animator.setDuration(300);// 设置周期
// 设置监听
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
// 每次值变化后会回调此方法
@Override
public void onAnimationUpdate(ValueAnimator animator) {
int height = (int) animator.getAnimatedValue();
// 重新设置LineartLayout的高度从而实现动画效果
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) llContainer.getLayoutParams();
params.height = height;
llContainer.setLayoutParams(params);
}
});
//动画执行后滚动至底部,设置监听
animator.addListener(new Animator.AnimatorListener() {
//动画结束后回调
@Override
public void onAnimationEnd(Animator animator) {
if (animationFinishListener != null){
animationFinishListener.onFinish();
}
}
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
});
animator.start();
}
// 箭头的动画
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) {
if (isExpand) {
hide(true);
tvTip.setText(hideText);
} else {
expand();
tvTip.setText(expandText);
}
doArrowAnim();
isExpand = !isExpand;
}
private AnimationFinishListener animationFinishListener;
public void setOnAnimationFinishListener(AnimationFinishListener animationFinishListener) {
this.animationFinishListener = animationFinishListener;
}
/**动画完成后的监听*/
public interface AnimationFinishListener {
void onFinish();
}
}
package com.chaychan.viewlib;
import android.text.Editable;
import android.text.Selection;
import android.text.TextWatcher;
import android.widget.EditText;
/**
* 银行卡号输入框格式(每4位有个空格)
*
* @author Administrator
*/
class NumberTextWatcher implements TextWatcher {
private EditText mEditText;
private char mDivider;
int beforeTextLength = 0;
int onTextLength = 0;
boolean isChanged = false;
int location = 0;// 记录光标的位置
private char[] tempChar;
private StringBuffer buffer = new StringBuffer();
int konggeNumberB = 0;
public NumberTextWatcher(EditText editText, char divider) {
mEditText = editText;
mDivider = divider;
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
onTextLength = s.length();
buffer.append(s.toString());
if (onTextLength == beforeTextLength || onTextLength <= 3
|| isChanged) {
isChanged = false;
return;
}
isChanged = true;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
beforeTextLength = s.length();
if (buffer.length() > 0) {
buffer.delete(0, buffer.length());
}
konggeNumberB = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == mDivider) {
konggeNumberB++;
}
}
}
@Override
public void afterTextChanged(Editable s) {
if (isChanged) {
location = mEditText.getSelectionEnd();
int index = 0;
while (index < buffer.length()) {
if (buffer.charAt(index) == mDivider) {
buffer.deleteCharAt(index);
} else {
index++;
}
}
index = 0;
int spaceNumberCount = 0;//空格的个数
while (index < buffer.length()) {
if ((index == 4 || index == 9 || index == 14 || index == 19 || index == 24 || index == 29)) {
buffer.insert(index, mDivider);
spaceNumberCount++;
}
index++;
}
if (spaceNumberCount > konggeNumberB) {
location += (spaceNumberCount - konggeNumberB);
}
tempChar = new char[buffer.length()];
buffer.getChars(0, buffer.length(), tempChar, 0);
String str = buffer.toString();
if (location > str.length()) {
location = str.length();
} else if (location < 0) {
location = 0;
}
mEditText.setText(str);
Editable etable = mEditText.getText();
Selection.setSelection(etable, location);
isChanged = false;
}
}
}
\ No newline at end of file
package com.chaychan.viewlib;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.CycleInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.EditText;
public class PhoneEditText extends EditText implements View.OnFocusChangeListener {
private int lastLength = 0;
private TextWatcher mTextWatcher;
/**
* 删除按钮的引用
*/
private Drawable mClearDrawable;
/**
* 控件是否有焦点
*/
private boolean hasFoucs;
private String divider = " ";
public PhoneEditText(Context context) {
this(context, null);
}
public PhoneEditText(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.editTextStyle); // Attention here !
}
public PhoneEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PhoneEditText);
String dividerString = typedArray.getString(R.styleable.PhoneEditText_dividerString);
if (dividerString != null && dividerString.length() > 0){
divider = dividerString;
}
typedArray.recycle();
initContent();
}
private void initContent() {
mTextWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(hasFoucs){
//setClearIconVisible(s.length() > 0);
}
}
@Override
public void afterTextChanged(Editable s) {
if (s.length() < lastLength){
lastLength = s.length();
return;
}
//防止进入死循环
removeTextChangedListener(mTextWatcher);
String text = getText().toString().trim();
if (text.contains(divider)){//删除已添加的divider
String[] ses = text.split(divider);
for (int j = 0; j< ses.length -1;j++){
int length = 0;
for (int k = 0; k <= j;k++){
length = length + ses[k].length();
}
s.delete(length,length + divider.length());
}
}
if (s.length() > 3){//插入divider
s.insert(3,divider);
}
if (s.length() > (7 + divider.length())){
s.insert((7 + divider.length()),divider);
}
if (s.length() > (11 + divider.length()*2)){//删除最后添加的divider
s.delete((11 + divider.length()*2),getText().length());
}
lastLength = s.length();
addTextChangedListener(mTextWatcher);
}
};
addTextChangedListener(mTextWatcher);
//获取EditText的DrawableRight,假如没有设置我们就使用默认的图片
mClearDrawable = getCompoundDrawables()[2];
if (mClearDrawable == null) {
// throw new NullPointerException("You can add drawableRight attribute in XML");
mClearDrawable = getResources().getDrawable(R.drawable.delete_selector);
}
mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight());
//默认设置隐藏图标
setClearIconVisible(false);
//设置焦点改变的监听
setOnFocusChangeListener(this);
}
/**
*
*/
public String getTextString(){
return getText().toString().replace(divider,"");
}
/**
* 因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件
* 当我们按下的位置 在 EditText的宽度 - 图标到控件右边的间距 - 图标的宽度 和
* EditText的宽度 - 图标到控件右边的间距之间我们就算点击了图标,竖直方向就没有考虑
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (getCompoundDrawables()[2] != null) {
boolean touchable = event.getX() > (getWidth() - getTotalPaddingRight())
&& (event.getX() < ((getWidth() - getPaddingRight())));
if (touchable) {
this.setText("");
}
}
}
return super.onTouchEvent(event);
}
/**
* 当ClearEditText焦点发生变化的时候,判断里面字符串长度设置清除图标的显示与隐藏
*/
@Override
public void onFocusChange(View v, boolean hasFocus) {
this.hasFoucs = hasFocus;
// if (hasFocus) {
// setClearIconVisible(getText().length() > 0);
// } else {
// setClearIconVisible(false);
// }
}
/**
* 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去
* @param visible
*/
protected void setClearIconVisible(boolean visible) {
Drawable right = visible ? mClearDrawable : null;
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], right, getCompoundDrawables()[3]);
}
/**
* 设置晃动动画
*/
public void setShakeAnimation(){
this.setAnimation(shakeAnimation(50));
}
/**
* 晃动动画
* @param counts 1秒钟晃动多少下
* @return
*/
public static Animation shakeAnimation(int counts){
Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0);
translateAnimation.setInterpolator(new CycleInterpolator(counts));
translateAnimation.setDuration(1000);
return translateAnimation;
}
}
\ No newline at end of file
......@@ -14,8 +14,12 @@ import android.widget.EditText;
public class PowerfulEditText extends EditText {
private static final String TAG = PowerfulEditText.class.getSimpleName();
/**普通类型*/
private static final int TYPE_NORMAL = -1;
/**自带清除功能的类型*/
private static final int TYPE_CAN_CLEAR = 0;
/**自带密码查看功能的类型*/
private static final int TYPE_CAN_WATCH_PWD = 1;
......@@ -131,7 +135,8 @@ public class PowerfulEditText extends EditText {
@Override
public void onTextChanged(CharSequence s, int start, int count,
int after) {
if (funcType == 0) {
//如果是带有清除功能的类型,当文本内容发生变化的时候,根据内容的长度是否为0进行隐藏或显示
if (funcType == TYPE_CAN_CLEAR) {
setRightIconVisible(s.length() > 0);
}
......@@ -157,6 +162,8 @@ public class PowerfulEditText extends EditText {
});
}
ta.recycle();
}
......@@ -170,17 +177,17 @@ public class PowerfulEditText extends EditText {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (getCompoundDrawables()[2] != null) {
boolean touchable = event.getX() > (getWidth() - getTotalPaddingRight())
boolean isTouched = event.getX() > (getWidth() - getTotalPaddingRight())
&& (event.getX() < ((getWidth() - getPaddingRight())));
if (touchable) {
if (isTouched) {
if (onRightClickListener == null) {
if (funcType == TYPE_CAN_CLEAR) {
//如果没有设置右边图标的点击事件,并且带有清除功能,默认清除文本
this.setText("");
} else if (funcType == TYPE_CAN_WATCH_PWD) {
//如果没有设置右边图标的点击事件,并且带有查看密码功能,点击查看密码
//如果没有设置右边图标的点击事件,并且带有查看密码功能,点击切换密码查看方式
if (eyeOpen) {
//变为密文 TYPE_CLASS_TEXT 和 TYPE_TEXT_VARIATION_PASSWORD 必须一起使用
this.setTransformationMethod(PasswordTransformationMethod.getInstance());
......@@ -256,6 +263,7 @@ public class PowerfulEditText extends EditText {
* 输入框文本变化的回调,如果需要进行多一些操作判断,则设置此listen替代TextWatcher
*/
public interface TextListener {
void onTextChanged(CharSequence s, int start, int count, int after);
void beforeTextChanged(CharSequence s, int start, int count, int after);
......
......@@ -23,4 +23,15 @@ public class UIUtils {
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);
}
}
<?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="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
></LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="30dp"
android:id="@+id/rl_bottom"
>
<TextView
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查看更多"
android:layout_centerInParent="true"
/>
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/arrow_down"
android:layout_toRightOf="@id/tv_tip"
android:layout_centerInParent="true"
android:layout_marginLeft="5dp"
/>
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
<?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" />
</declare-styleable>
<declare-styleable name="PowerfulEditText">
<!--功能的类型-->
<attr name="funcType">
......@@ -20,4 +34,9 @@
<!--右侧Drawable的高度-->
<attr name="rightDrawableHeight" format="dimension"/>
</declare-styleable>
<declare-styleable name="PhoneEditText">
<attr name="dividerString" format="string" />
</declare-styleable>
</resources>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册