編輯:關於Android編程

在國內很多應用中,也可以看到這種效果的使用場景。如豌豆莢的詳情頁:

一、項目使用
(1).在工程的build.gradle文件中添加項目引用。
該項目較簡單,java代碼部分只有ExpandableTextView.java一個文件。
dependencies {
compile 'com.ms-square:expandableTextView:0.1.4'
}
(2).在布局中添加如下代碼。ExpandableTextView支持使用自定義屬性。對應的attrs.xml文件及屬性釋義如下。
(3).添加Java代碼。
ExpandableTextView expandableTextView = (ExpandableTextView) rootView.findViewById(R.id.expand_text_view);
// 設置顯示內容
expandableTextView.setText("");
// 設置狀態監聽
expandableTextView.setOnExpandStateChangeListener(new ExpandableTextView.OnExpandStateChangeListener() {
@Override
public void onExpandStateChanged(TextView textView, boolean isExpanded) {
}
});
ExpandableTextView繼承自LinearLayout類,實現了OnClickListener接口,監聽自身的點擊事件,處理“展開”和“收起”。
public class ExpandableTextView extends LinearLayout implements View.OnClickListener {
}
// 默認行數 private static final int MAX_COLLAPSED_LINES = 8; // 默認動畫時長 private static final int DEFAULT_ANIM_DURATION = 300; // 動畫啟動時TextView的默認透明度 private static final float DEFAULT_ANIM_ALPHA_START = 0.7f; // 顯示內容的TextView protected TextView mTv; // 箭頭按鈕ImageButton protected ImageButton mButton; // 是否需要重新布局。當調用了setText()方法後,該值置為true private boolean mRelayout; // 默認TextView處於收起狀態 private boolean mCollapsed = true; // 收起狀態下的整個View的高度 private int mCollapsedHeight; // TextView整個文本的高度 private int mTextHeightWithMaxLines; // 收起狀態下的最大顯示行數 private int mMaxCollapsedLines; // TextView的bottomMargin private int mMarginBetweenTxtAndBottom; // 箭頭按鈕的展開圖標 private Drawable mExpandDrawable; // 箭頭按鈕的收起圖標 private Drawable mCollapseDrawable; // 動畫執行時長 private int mAnimationDuration; // 動畫啟動時顯示內容的透明度 private float mAnimAlphaStart; // 是否正在執行動畫 private boolean mAnimating; // 狀態改變的監聽 private OnExpandStateChangeListener mListener; // 如果是在ListView中,需要使用到mCollapsedStatus和mPosition,保存當前position的展開或收起狀態 private SparseBooleanArray mCollapsedStatus; private int mPosition;
public ExpandableTextView(Context context) {
this(context, null);
}
public ExpandableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public ExpandableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
mMaxCollapsedLines = typedArray.getInt(R.styleable.ExpandableTextView_maxCollapsedLines, MAX_COLLAPSED_LINES);
mAnimationDuration = typedArray.getInt(R.styleable.ExpandableTextView_animDuration, DEFAULT_ANIM_DURATION);
mAnimAlphaStart = typedArray.getFloat(R.styleable.ExpandableTextView_animAlphaStart, DEFAULT_ANIM_ALPHA_START);
mExpandDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_expandDrawable);
mCollapseDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_collapseDrawable);
if (mExpandDrawable == null) {
mExpandDrawable = getDrawable(getContext(), R.drawable.ic_expand_more_black_12dp);
}
if (mCollapseDrawable == null) {
mCollapseDrawable = getDrawable(getContext(), R.drawable.ic_expand_less_black_12dp);
}
typedArray.recycle();
// 強制把方向設置為垂直
setOrientation(LinearLayout.VERTICAL);
// 默認不顯示
setVisibility(GONE);
}
當xml布局文件加載完成之後,後執行onFinishInflate()方法,在這裡完成View的初始化。
@Override
protected void onFinishInflate() {
findViews();
}
private void findViews() {
mTv = (TextView) findViewById(R.id.expandable_text);
mTv.setOnClickListener(this);
mButton = (ImageButton) findViewById(R.id.expand_collapse);
mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);
mButton.setOnClickListener(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 如果沒有改變顯示內容,或者顯示內容為空,執行super.onMeasure()並返回
if (!mRelayout || getVisibility() == View.GONE) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
mRelayout = false;
// 先隱藏箭頭按鈕,將文字最大顯示行數設置到最大,後面再根據測量情況修改
mButton.setVisibility(View.GONE);
mTv.setMaxLines(Integer.MAX_VALUE);
// 測量
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 如果內容未超出限定的最大顯示行數,直接返回。在這種情況下,無需展開和收起。
if (mTv.getLineCount() <= mMaxCollapsedLines) {
return;
}
// 計算TextView的真實高度並保存
mTextHeightWithMaxLines = getRealTextViewHeight(mTv);
// 限定TextView的最大行數,並將箭頭按鈕顯示出來
if (mCollapsed) {
mTv.setMaxLines(mMaxCollapsedLines);
}
mButton.setVisibility(View.VISIBLE);
// 再次重新測量
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mCollapsed) {
mTv.post(new Runnable() {
@Override
public void run() {
// TextView的bottomMargin
mMarginBetweenTxtAndBottom = getHeight() - mTv.getHeight();
}
});
// 保存收起狀態下的整個View的高度
mCollapsedHeight = getMeasuredHeight();
}
}
// 獲取TextView的真實高度,即內容展開時的高度
private static int getRealTextViewHeight(@NonNull TextView textView) {
int textHeight = textView.getLayout().getLineTop(textView.getLineCount());
int padding = textView.getCompoundPaddingTop() + textView.getCompoundPaddingBottom();
return textHeight + padding;
}
// 實現了展開/收起功能的動畫
class ExpandCollapseAnimation extends Animation {
private final View mTargetView;
private final int mStartHeight;
private final int mEndHeight;
public ExpandCollapseAnimation(View view, int startHeight, int endHeight) {
mTargetView = view;
mStartHeight = startHeight;
mEndHeight = endHeight;
setDuration(mAnimationDuration);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// 計算在執行動畫時,逐漸變化產生的整個View的新高度
final int newHeight = (int)((mEndHeight - mStartHeight) * interpolatedTime + mStartHeight);
// 修改TextView的最大高度
mTv.setMaxHeight(newHeight - mMarginBetweenTxtAndBottom);
// 如果初始設置了透明度,修改TextView的透明度,直到alpha==1完全不透明
if (Float.compare(mAnimAlphaStart, 1.0f) != 0) {
applyAlphaAnimation(mTv, mAnimAlphaStart + interpolatedTime * (1.0f - mAnimAlphaStart));
}
// 修改整個View的高度
mTargetView.getLayoutParams().height = newHeight;
mTargetView.requestLayout();
}
}
// 改變View的透明度
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static void applyAlphaAnimation(View view, float alpha) {
if (isPostHoneycomb()) {
// setAlpha()方法是Android3.0以上才有的api
view.setAlpha(alpha);
} else {
// 在Android3.0以下,使用AlphaAnimation並將Duration設置為0來實現
AlphaAnimation alphaAnimation = new AlphaAnimation(alpha, alpha);
alphaAnimation.setDuration(0);
alphaAnimation.setFillAfter(true);
view.startAnimation(alphaAnimation);
}
}
private static boolean isPostHoneycomb() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
@Override
public void onClick(View view) {
// 文字內容太短,無需折疊時,箭頭按鈕是隱藏的。這時直接返回。
if (mButton.getVisibility() != View.VISIBLE) {
return;
}
// 將變量mCollapsed和mButton顯示的圖片賦值為相反
mCollapsed = !mCollapsed;
mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);
// 將當前位置的狀態保存起來,在ListView中需要使用
if (mCollapsedStatus != null) {
mCollapsedStatus.put(mPosition, mCollapsed);
}
// 標記動畫開始
mAnimating = true;
Animation animation;
if (mCollapsed) {
// “收起”動畫,從當前高度開始,降低到收起狀態下的高度mCollapsedHeight
animation = new ExpandCollapseAnimation(this, getHeight(), mCollapsedHeight);
} else {
// “展開”動畫,在當前整個View高度的基礎上,增加TextView變化的高度(mTextHeightWithMaxLines - mTv.getHeight())
animation = new ExpandCollapseAnimation(this, getHeight(), getHeight() +
mTextHeightWithMaxLines - mTv.getHeight());
}
animation.setFillAfter(true);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// 動畫開始,改變TextView的透明度
applyAlphaAnimation(mTv, mAnimAlphaStart);
}
@Override
public void onAnimationEnd(Animation animation) {
// 清除動畫,防止applyTransformation()重復執行
clearAnimation();
// 標記動畫結束
mAnimating = false;
// 通知listener
if (mListener != null) {
mListener.onExpandStateChanged(mTv, !mCollapsed);
}
}
@Override
public void onAnimationRepeat(Animation animation) { }
});
// 清除之前的動畫,開啟新的動畫
clearAnimation();
startAnimation(animation);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 動畫執行期間,攔截掉觸摸事件,不傳遞給子View
return mAnimating;
}
// 設置顯示內容
public void setText(@Nullable CharSequence text) {
mRelayout = true;
mTv.setText(text);
// 內容不為空時,才會顯示
setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);
}
// 設置顯示內容,在ListView等存在item復用的情況下需要使用該方法
public void setText(@Nullable CharSequence text, @NonNull SparseBooleanArray collapsedStatus, int position) {
mCollapsedStatus = collapsedStatus;
mPosition = position;
boolean isCollapsed = collapsedStatus.get(position, true);
clearAnimation();
mCollapsed = isCollapsed;
mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);
setText(text);
getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
requestLayout();
}
public void setOnExpandStateChangeListener(@Nullable OnExpandStateChangeListener listener) {
mListener = listener;
}
// 狀態改變的監聽器
public interface OnExpandStateChangeListener {
/**
* 當展開或收起的動畫執行完畢之後,該方法被回調
* @param textView - 展開或收起的TextView
* @param isExpanded - 如果文字被展開,該值為true;否則為false
*/
void onExpandStateChanged(TextView textView, boolean isExpanded);
}
@Override
public void setOrientation(int orientation){
if(LinearLayout.HORIZONTAL == orientation){
throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation.");
}
super.setOrientation(orientation);
}
Android 中使用代碼動態布局
Android 中使用代碼動態布局 本文介紹在android中使用代碼動態布局,有時候根據不同的需求,比如需要根據服務器上的條目個數來決定app中頁面布局控件(
android-async-http開源項目對服務器端返回JSON數據的處理
一、在JavaEE項目中搭建環境 1. 導入相關jar包 2. 搭建相關的包和類 UserDao: UserDaoImpl: JsonServlet
android圖片處理之讓圖片一直勻速旋轉
本文是在我的文章android圖片處理,讓圖片變成圓形 的基礎上繼續寫的,可以去看看,直接看也沒關系,也能看懂 1、首先在res文件夾下創建一個名字為anim的
Android RSA加密解密
概述 RSA是目前最有影響力的公鑰加密算法,該算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困 難,因此可以