Java tutorial
/* * Copyright (C) 2011 The Android Open Source Project * Copyright 2014 Manabu Shimobe * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jaydenxiao.androidfire.ui.zone.widget; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.util.AttributeSet; import android.util.SparseBooleanArray; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import com.jaydenxiao.androidfire.R; import com.jaydenxiao.common.commonutils.DisplayUtil; public class ExpandableTextView extends LinearLayout implements View.OnClickListener { /* */ private static final int MAX_COLLAPSED_LINES = 5; /* */ private static final int DEFAULT_ANIM_DURATION = 200; /*textview*/ protected TextView mTvContent; /*textview*/ protected TextView mTvExpandCollapse; /*??*/ private boolean mRelayout; /**/ private boolean mCollapsed = true; /**/ private Drawable mExpandDrawable; /**/ private Drawable mCollapseDrawable; /**/ private int mAnimationDuration; /*?*/ private boolean mAnimating; /* ? */ private OnExpandStateChangeListener mListener; /* listview??item/? */ private SparseBooleanArray mCollapsedStatus; /* ? */ private int mPosition; /*??*/ private int mMaxCollapsedLines; /*linerlayout*/ private int mCollapsedHeight; /*tv?padding*/ private int mTextHeightWithMaxLines; /*tvMarginTopAmndBottom*/ private int mMarginBetweenTxtAndBottom; /**/ private int contentTextColor; /**/ private int collapseExpandTextColor; /*?*/ private float contentTextSize; /*?*/ private float collapseExpandTextSize; /**/ private String textCollapse; /**/ private String textExpand; //? private boolean showExpandCollapseDrawable = true; /*?*/ private int grarity; 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); } @Override public void setOrientation(int orientation) { if (LinearLayout.HORIZONTAL == orientation) { throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation."); } super.setOrientation(orientation); } /** * ? * @param attrs */ private void init(AttributeSet attrs) { mCollapsedStatus = new SparseBooleanArray(); 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); mExpandDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_expandDrawable); mCollapseDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_collapseDrawable); textCollapse = typedArray.getString(R.styleable.ExpandableTextView_textCollapse); textExpand = typedArray.getString(R.styleable.ExpandableTextView_textExpand); showExpandCollapseDrawable = typedArray .getBoolean(R.styleable.ExpandableTextView_showExpandCollapseDrawable, true); if (showExpandCollapseDrawable) { if (mExpandDrawable == null) { mExpandDrawable = ContextCompat.getDrawable(getContext(), R.drawable.icon_green_arrow_down); } if (mCollapseDrawable == null) { mCollapseDrawable = ContextCompat.getDrawable(getContext(), R.drawable.icon_green_arrow_up); } } if (TextUtils.isEmpty(textCollapse)) { textCollapse = getContext().getString(R.string.shink); } if (TextUtils.isEmpty(textExpand)) { textExpand = getContext().getString(R.string.expand); } contentTextColor = typedArray.getColor(R.styleable.ExpandableTextView_contentTextColor, ContextCompat.getColor(getContext(), R.color.light_gray)); contentTextSize = typedArray.getDimension(R.styleable.ExpandableTextView_contentTextSize, DisplayUtil.sp2px(14)); collapseExpandTextColor = typedArray.getColor(R.styleable.ExpandableTextView_collapseExpandTextColor, ContextCompat.getColor(getContext(), R.color.main_color)); collapseExpandTextSize = typedArray.getDimension(R.styleable.ExpandableTextView_collapseExpandTextSize, DisplayUtil.sp2px(14)); grarity = typedArray.getInt(R.styleable.ExpandableTextView_collapseExpandGrarity, Gravity.LEFT); typedArray.recycle(); // enforces vertical orientation setOrientation(LinearLayout.VERTICAL); // default visibility is gone setVisibility(GONE); } /** * ??view */ @Override protected void onFinishInflate() { findViews(); } /** * ?viwe */ private void findViews() { LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.item_expand_shink, this); mTvContent = (TextView) findViewById(R.id.expandable_text); mTvContent.setOnClickListener(this); mTvExpandCollapse = (TextView) findViewById(R.id.expand_collapse); if (showExpandCollapseDrawable) { mTvExpandCollapse.setCompoundDrawablesWithIntrinsicBounds(null, null, mCollapsed ? mExpandDrawable : mCollapseDrawable, null); } mTvExpandCollapse.setText( mCollapsed ? getResources().getString(R.string.expand) : getResources().getString(R.string.shink)); mTvExpandCollapse.setOnClickListener(this); mTvContent.setTextColor(contentTextColor); mTvContent.getPaint().setTextSize(contentTextSize); mTvExpandCollapse.setTextColor(collapseExpandTextColor); mTvExpandCollapse.getPaint().setTextSize(collapseExpandTextSize); //?? LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = grarity; mTvExpandCollapse.setLayoutParams(lp); } /** * * @param view */ @Override public void onClick(View view) { if (mTvExpandCollapse.getVisibility() != View.VISIBLE) { return; } mCollapsed = !mCollapsed; ///? if (showExpandCollapseDrawable) { mTvExpandCollapse.setCompoundDrawablesWithIntrinsicBounds(null, null, mCollapsed ? mExpandDrawable : mCollapseDrawable, null); } mTvExpandCollapse.setText( mCollapsed ? getResources().getString(R.string.expand) : getResources().getString(R.string.shink)); //??? if (mCollapsedStatus != null) { mCollapsedStatus.put(mPosition, mCollapsed); } // / mAnimating = true; ValueAnimator valueAnimator; if (mCollapsed) { // mTvContent.setMaxLines(mMaxCollapsedLines); valueAnimator = new ValueAnimator().ofInt(getHeight(), mCollapsedHeight); } else { valueAnimator = new ValueAnimator().ofInt(getHeight(), getHeight() + mTextHeightWithMaxLines - mTvContent.getHeight()); } valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { int animatedValue = (int) valueAnimator.getAnimatedValue(); mTvContent.setMaxHeight(animatedValue - mMarginBetweenTxtAndBottom); getLayoutParams().height = animatedValue; requestLayout(); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { // ?????? /// clear the animation flag mAnimating = false; // notify the listener if (mListener != null) { mListener.onExpandStateChanged(mTvContent, !mCollapsed); } } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); valueAnimator.setDuration(mAnimationDuration); valueAnimator.start(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // ??child? return mAnimating; } /** * ?? * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // If no change, measure and return if (!mRelayout || getVisibility() == View.GONE) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } mRelayout = false; // Setup with optimistic case // i.e. Everything fits. No button needed mTvExpandCollapse.setVisibility(View.GONE); mTvContent.setMaxLines(Integer.MAX_VALUE); // Measure super.onMeasure(widthMeasureSpec, heightMeasureSpec); //??? if (mTvContent.getLineCount() <= mMaxCollapsedLines) { return; } // ?tv?padding mTextHeightWithMaxLines = getRealTextViewHeight(mTvContent); // ?? if (mCollapsed) { mTvContent.setMaxLines(mMaxCollapsedLines); } mTvExpandCollapse.setVisibility(View.VISIBLE); // Re-measure with new setup super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mCollapsed) { // Gets the margin between the TextView's bottom and the ViewGroup's bottom mTvContent.post(new Runnable() { @Override public void run() { mMarginBetweenTxtAndBottom = getHeight() - mTvContent.getHeight(); } }); // ?? mCollapsedHeight = getMeasuredHeight(); } } /** * ?tv?padding * @param textView * @return */ private static int getRealTextViewHeight(TextView textView) { int textHeight = textView.getLayout().getLineTop(textView.getLineCount()); int padding = textView.getCompoundPaddingTop() + textView.getCompoundPaddingBottom(); return textHeight + padding; } /********************/ /** * /? * @param listener */ public void setOnExpandStateChangeListener(OnExpandStateChangeListener listener) { mListener = listener; } /** * * @param text */ public void setText(CharSequence text) { mRelayout = true; mTvContent.setText(text); setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE); } /** * ??/? * @param text * @param position */ public void setText(CharSequence text, int position) { mPosition = position; //??true: mCollapsed = mCollapsedStatus.get(position, true); clearAnimation(); /// if (showExpandCollapseDrawable) { mTvExpandCollapse.setCompoundDrawablesWithIntrinsicBounds(null, null, mCollapsed ? mExpandDrawable : mCollapseDrawable, null); } mTvExpandCollapse.setText( mCollapsed ? getResources().getString(R.string.expand) : getResources().getString(R.string.shink)); setText(text); getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; requestLayout(); } /** * ? * @return */ public CharSequence getText() { if (mTvContent == null) { return ""; } return mTvContent.getText(); } /** * ??? */ public interface OnExpandStateChangeListener { /** * @param textView - TextView being expanded/collapsed * @param isExpanded - true if the TextView has been expanded */ void onExpandStateChanged(TextView textView, boolean isExpanded); } }