Android开发之仿微信显示更多文字的View

时间:2022-05-27 10:16:22

最近开发需求中要模仿微信朋友圈文章的展开收起功能,网上找了找,发现都有问题,于是乎自己在前辈的基础上进行了一定量的修改,下边将源码贴出来供大家参考:
1.主Activity布局文件就不粘贴了,很简单,就一个ListView.
2.主Activity功能实现:

  1. <font face="宋体" size="3">package com.example.textviewdemo;
  2. import java.util.HashMap;
  3. import android.app.Activity;
  4. import android.content.Context;
  5. import android.os.Bundle;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.ViewGroup;
  9. import android.widget.BaseAdapter;
  10. import android.widget.ListView;
  11. import android.widget.TextView;
  12. import android.widget.TextView.BufferType;
  13. public class MainActivity extends Activity {
  14. String mStr;
  15. int type;
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20. Globl.map = new HashMap<Integer, Boolean>();
  21. ListView listview = (ListView) findViewById(R.id.listview);
  22. mStr = "手指在ListView上下滚动时,ListViewItem背景变黑,因为在滚动的时候为了提升性能做了优化,为提高滚动的性能,Android 框    架在ListView中引入CacheColorHint属性。如果该值为非0,则说明该ListView绘制在单色不透明的背景上,在默认情况下该值 为        #191919,也就是黑色主题中的黑色背景颜色值,这样当ListView滚动的时候";
  23. listview.setAdapter(new MyListAdpter(this));
  24. }
  25. class MyListAdpter extends BaseAdapter {
  26. Context con;
  27. CollapsibleTextView tv;
  28. public MyListAdpter(Context con) {
  29. this.con = con;
  30. }
  31. @Override
  32. public int getCount() {
  33. // TODO Auto-generated method stub
  34. return 10;
  35. }
  36. @Override
  37. public Object getItem(int position) {
  38. // TODO Auto-generated method stub
  39. return null;
  40. }
  41. @Override
  42. public long getItemId(int position) {
  43. // TODO Auto-generated method stub
  44. return 0;
  45. }
  46. HashMap<Integer, View> hashM = new HashMap<Integer, View>();
  47. @Override
  48. public View getView(int position, View convertView, ViewGroup parent) {
  49. Holder holder = null;
  50. View view;
  51. if (hashM.get(position) == null) {
  52. holder = new Holder();
  53. view = LayoutInflater.from(con).inflate(R.layout.item_list, null);
  54. holder.tv = (CollapsibleTextView) view.findViewById(R.id.tv_text);
  55. holder.tvcount = (TextView) view.findViewById(R.id.tvcount);
  56. view.setTag(holder);
  57. hashM.put(position, view);
  58. } else {
  59. view = hashM.get(position);
  60. holder = (Holder) view.getTag();
  61. }
  62. // if (Globl.map.get(position) == false) {
  63. // Globl.map.put(position, false);
  64. // type = 2;
  65. // } else {
  66. // type = 1;
  67. // }
  68. // tv.setNowType(type);
  69. // int typeNow = tv.getNowType();
  70. holder.tvcount.setText(position + "");
  71. holder.tv.setDesc(mStr, holder.tv, BufferType.NORMAL);
  72. return view;
  73. }
  74. class Holder {
  75. CollapsibleTextView tv;
  76. TextView tvcount;
  77. }
  78. }
  79. }

3.自定义控件CollapsibleTextView 源码:

  1. <font face="宋体" size="3">/**
  2. * @Explain: Text过长收起 带有查看全文/收起功能控件;
  3. * @Author:LYl
  4. * @Time:2014-11-27 下午4:33:05
  5. * @Version V2.1.54
  6. */
  7. public class CollapsibleTextView extends LinearLayout implements
  8. OnClickListener {
  9. /** 最大显示的行数 */
  10. private static final int DEFAULT_MAX_LINE_COUNT = 8;
  11. /** 实际展示的行数 */
  12. private static final int DEFAULT_SHOW_LINE_COUNT = 6;
  13. private static final int COLLAPSIBLE_STATE_NONE = 0;
  14. /** View处于展开状态 **/
  15. private static final int COLLAPSIBLE_STATE_SHRINKUP = 1;
  16. /** view收缩时状态 **/
  17. private static final int COLLAPSIBLE_STATE_SPREAD = 2;
  18. /** 显示内容的View */
  19. private TextView tv_context;
  20. /** 展开/收起按钮 */
  21. private TextView bt_spread;
  22. private String shrinkup;
  23. private String spread;
  24. /** 当前正处于的状态 */
  25. // private int mState;
  26. private boolean flag;
  27. private int nowType;
  28. private CollapsibleTextView coTextView;
  29. /** 判断是不是点击了查看更多、收起 */
  30. private boolean isClicke = false;
  31. private int lookCount = 0;
  32. public CollapsibleTextView(Context context, AttributeSet attrs) {
  33. super(context, attrs);
  34. shrinkup = "收起";
  35. spread = "查看全文";
  36. View view = inflate(context, R.layout.collapsible_textview, this);
  37. view.setPadding(0, -1, 0, 0);
  38. tv_context = (TextView) view.findViewById(R.id.tv_context);
  39. bt_spread = (TextView) view.findViewById(R.id.bt_spread);
  40. bt_spread.setOnClickListener(this);
  41. }
  42. public CollapsibleTextView(Context context) {
  43. this(context, null);
  44. }
  45. /**
  46. * 赋值
  47. */
  48. public final void setDesc(CharSequence charSequence,
  49. CollapsibleTextView tv, BufferType bufferType) {
  50. this.coTextView = tv;
  51. // 对内容中的网址进行处理;
  52. tv_context.setAutoLinkMask(Linkify.WEB_URLS);
  53. tv_context.setMovementMethod(LinkMovementMethod.getInstance());
  54. tv_context.setText(charSequence, bufferType);
  55. // 初始类型
  56. if (lookCount == 0) {
  57. coTextView.setNowType(COLLAPSIBLE_STATE_SPREAD);
  58. }
  59. lookCount += 1;
  60. // TODO LYL 放到ListView中需要加下句:falg=false;一般情况去掉就可
  61. flag = false;
  62. requestLayout();
  63. }
  64. @Override
  65. public void onClick(View v) {
  66. flag = false;
  67. isClicke = true;
  68. requestLayout();
  69. }
  70. @Override
  71. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  72. super.onLayout(changed, l, t, r, b);
  73. if (!flag) {
  74. flag = true;
  75. if (tv_context.getLineCount() <= DEFAULT_MAX_LINE_COUNT) {
  76. bt_spread.setVisibility(View.GONE);
  77. tv_context.setMaxLines(DEFAULT_MAX_LINE_COUNT + 1);
  78. coTextView.setNowType(COLLAPSIBLE_STATE_NONE);
  79. } else {
  80. post(new InnerRunnable());
  81. }
  82. }
  83. }
  84. class InnerRunnable implements Runnable {
  85. @Override
  86. public void run() {
  87. int zType = 0;
  88. // 第一次进入操作(没有点击并且是第一次进入);
  89. System.out.println("lookCount:" + lookCount);
  90. if (!isClicke && lookCount == 1) {
  91. if (coTextView.getNowType() == COLLAPSIBLE_STATE_SPREAD) {
  92. tv_context.setMaxLines(DEFAULT_SHOW_LINE_COUNT);
  93. bt_spread.setVisibility(View.VISIBLE);
  94. bt_spread.setText(spread);
  95. zType = COLLAPSIBLE_STATE_SHRINKUP;
  96. } else if (coTextView.getNowType() == COLLAPSIBLE_STATE_SHRINKUP) {
  97. tv_context.setMaxLines(Integer.MAX_VALUE);
  98. bt_spread.setVisibility(View.VISIBLE);
  99. bt_spread.setText(shrinkup);
  100. zType = COLLAPSIBLE_STATE_SPREAD;
  101. }
  102. coTextView.setNowType(zType);
  103. // 点击了查看更多、收起转换状态;
  104. } else if (isClicke) {
  105. isClicke = false;
  106. if (coTextView.getNowType() == COLLAPSIBLE_STATE_SPREAD) {
  107. tv_context.setMaxLines(DEFAULT_SHOW_LINE_COUNT);
  108. bt_spread.setVisibility(View.VISIBLE);
  109. bt_spread.setText(spread);
  110. coTextView.setNowType(COLLAPSIBLE_STATE_SHRINKUP);
  111. } else if (coTextView.getNowType() == COLLAPSIBLE_STATE_SHRINKUP) {
  112. tv_context.setMaxLines(Integer.MAX_VALUE);
  113. bt_spread.setVisibility(View.VISIBLE);
  114. bt_spread.setText(shrinkup);
  115. coTextView.setNowType(COLLAPSIBLE_STATE_SPREAD);
  116. }
  117. // 滑动listView 从新载入到可见界面 不做操作,保持原有状态;(为了后面看得人能够更理解写上)
  118. } else if (!isClicke && lookCount != 1) {
  119. }
  120. }
  121. }
  122. public int getNowType() {
  123. return nowType;
  124. }
  125. public void setNowType(int nowType) {
  126. this.nowType = nowType;
  127. }
  128. }



public class CollapsiblePanel extends LinearLayout {
    /**
     * TAG
     */
    private static final String TAG = "CollapsiblePanel";
    /**
     * DEBUG
     */
    private static final boolean DEBUG = BuildConfig.DEBUG;
    /**
     * 内容View
     */
    private View mContentView;
    /**
     * 可收缩的View
     */
    private View mCollapsibleView;
    /**
     * 可收缩的大小
     */
    private int mCollapsibleSize;
    /**
     * 收缩的监听器
     */
    private OnCollapsibleListener mCollapsibleListener;
    /**
     * 收缩动画的时间
     */
    private int mAnimDuration = 0;  //SUPPRESS CHECKSTYLE
    /**
     * 判断当前可收缩View是否是打开状态
     */
    private boolean mIsOpened = false;
    /**
     * 可收缩View默认是否可见
     */
    private boolean mCollapsibleViewDefaultVisible = false;
    /**
     * Toggle是否可用
     */
    private boolean mToggleEnable = true;
    /**
     * 不使用Alpha动画
     */
    private boolean mWithoutAlphaAnim = true;
    /**
     * 收缩是否有动画效果
     */
    private boolean mDoAnimation = true;

    public interface OnCollapsibleListener {
        /**
         * 动画结束监听
         *
         * @param isOpened 当前的stretchView是否是打开的,
         */
        void onCollapsibleFinished(boolean isOpened);

        /**
         * 动画过程中使用Transformation的接口
         *
         * @param from             from
         * @param to               to
         * @param interpolatedTime interpolatedTime
         */
        void applyTransformation(int from, int to, float interpolatedTime);
    }

    /**
     * 构造方法
     *
     * @param context context
     */
    public CollapsiblePanel(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * 构造方法
     *
     * @param context context
     * @param attrs   attrs
     */
    public CollapsiblePanel(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * 构造方法
     *
     * @param context  context
     * @param attrs    attrs
     * @param defStyle defStyle
     */
    @SuppressLint("NewApi")
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public CollapsiblePanel(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    /**
     * 初始化
     *
     * @param context context
     * @param attrs   attrs
     */
    private void init(Context context, AttributeSet attrs) {
        setOrientation(LinearLayout.VERTICAL);
        mAnimDuration = 280;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mCollapsibleSize == 0 && mCollapsibleView != null) {
            mCollapsibleView.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);

            if (LinearLayout.VERTICAL == getOrientation()) {
                mCollapsibleSize = mCollapsibleView.getMeasuredHeight();
                if (!mCollapsibleViewDefaultVisible) {
                    mCollapsibleView.getLayoutParams().height = 0;
                }
            } else {
                mCollapsibleSize = mCollapsibleView.getMeasuredWidth();
                if (!mCollapsibleViewDefaultVisible) {
                    mCollapsibleView.getLayoutParams().width = 0;
                }
            }
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 设置控件初始时是否可见
     *
     * @param visible 是否可见
     */
    public void setCollapsibleViewDefaultVisible(boolean visible) {
        mCollapsibleViewDefaultVisible = visible;
        // 默认可见的话,则认为是展开的
        mIsOpened = visible;
    }

    /**
     * 控件初始时是否可见
     *
     * @return visible
     */
    public boolean getCollapsibleViewDefaultVisible() {
        return mCollapsibleViewDefaultVisible;
    }

    /**
     * 设置toggle是否可用,如果设置为false,则{@link #toggle()}接口无效
     *
     * @param enable enable
     */
    public void setToggleEnable(boolean enable) {
        mToggleEnable = enable;
    }

    /**
     * 获取当前的主View
     *
     * @return view
     */
    public View getContentView() {
        return mContentView;
    }

    /**
     * 获取当前的扩展View
     *
     * @return view
     */
    public View getStretchView() {
        return mCollapsibleView;
    }

    /**
     * 设置主View
     *
     * @param view view
     */
    public void setContentView(View view) {
        if (view != null) {
            if (mContentView != null) {
                removeView(this.mContentView);
            }

            mContentView = view;
            addView(mContentView, 0);
        }
    }

    /**
     * 设置收缩的View
     *
     * @param collapsibleView 可以收缩的View
     */
    public void setCollapsibleView(View collapsibleView) {
        if (collapsibleView != null) {
            if (mCollapsibleView != null) {
                removeView(mCollapsibleView);
                // 在重新设置时,将该值置为0,否则新view将不能显示正确的高度
                mCollapsibleSize = 0;
            }

            mCollapsibleView = collapsibleView;
            addView(mCollapsibleView);
        }
    }

    /**
     * 得到可收缩View的大小
     *
     * @return 可收缩View的大小
     */
    public int getCollapsibleSize() {
        return mCollapsibleSize;
    }

    /**
     * 设置收缩的监听
     *
     * @param listener listener
     */
    public void setOnCollapsibleListener(OnCollapsibleListener listener) {
        mCollapsibleListener = listener;
    }

    /**
     * 当前的视图是否已经展开
     *
     * @return true/false
     */
    public boolean isCollapsibleViewOpened() {
        return mIsOpened;
    }

    /**
     * 设置展开(或者收缩)动画的时间,默认280ms
     *
     * @param durationMs durationMs
     */
    public void setCollapsibleAnimDuration(int durationMs) {
        if (durationMs >= 0) {
            mAnimDuration = durationMs;
        } else {
            throw new IllegalArgumentException("Animation duration cannot be negative");
        }
    }

    /**
     * 展开/收起View
     *
     * @return true/false
     */
    public boolean toggle() {
        // 如果不允许展开
        if (!mToggleEnable) {
            return false;
        }

        // 如果动画正在进行,不执行任何操作
        if (isAnimationPlaying()) {
            return false;
        }

        if (mIsOpened) {
            closeCollapsibleView();
        } else {
            openCollapsibleView();
        }

        return true;
    }

    /**
     * 展开视图
     */
    public void openCollapsibleView() {
        if (mCollapsibleView == null) {
            return;
        }

        post(new Runnable() {
            @Override
            public void run() {
                if (mDoAnimation) {
                    CollapsibleAnimation animation = new CollapsibleAnimation(0, mCollapsibleSize, 0.0f, 1.0f);
                    animation.setDuration(mAnimDuration);
                    animation.setAnimationListener(mCollapsibleAnimListener);
                    mCollapsibleView.startAnimation(animation);
                    invalidate();
                } else {
                    setCollapsibleViewSize(mCollapsibleSize);
                    onCollapsibleEnd();
                }
            }
        });
    }

    /**
     * 收起视图
     */
    public void closeCollapsibleView() {
        if (mCollapsibleView == null) {
            return;
        }

        post(new Runnable() {
            @Override
            public void run() {
                if (mDoAnimation) {
                    CollapsibleAnimation animation = new CollapsibleAnimation(mCollapsibleSize, 0, 1.0f, 0.0f);
                    animation.setDuration(mAnimDuration);
                    animation.setAnimationListener(mCollapsibleAnimListener);
                    mCollapsibleView.startAnimation(animation);
                    invalidate();
                } else {
                    setCollapsibleViewSize(0);
                    onCollapsibleEnd();
                }
            }
        });
    }

    /**
     * 收缩View展开或收缩时调用
     *
     * @param isOpened isOpened
     */
    protected void onCollapsibleFinished(boolean isOpened) {

    }

    /**
     * 重置大小
     */
    protected void resetCollapsibleSize() {
        mCollapsibleSize = 0;
        requestLayout();
    }

    /**
     * 设置收缩View的大小
     *
     * @param size size
     */
    private void setCollapsibleViewSize(int size) {
        if (null == mCollapsibleView) {
            return;
        }

        LayoutParams params = (LayoutParams) mCollapsibleView.getLayoutParams();
        if (null != params) {
            if (LinearLayout.VERTICAL == getOrientation()) {
                params.height = size;
            } else {
                params.width = size;
            }

            mCollapsibleView.setLayoutParams(params);
        }
    }

    /**
     * 判断动画是否正在播放
     *
     * @return true/false
     */
    private boolean isAnimationPlaying() {
        if (null != mCollapsibleView) {
            Animation anim = mCollapsibleView.getAnimation();
            if (null != anim && !anim.hasEnded()) {
                return true;
            }
        }

        return false;
    }

    /**
     * 动画的监听器
     */
    private AnimationListener mCollapsibleAnimListener = new AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            onCollapsibleEnd();
        }
    };

    /**
     * 收缩结束时调用
     */
    private void onCollapsibleEnd() {
        mIsOpened = !mIsOpened;
        if (mCollapsibleListener != null) {
            mCollapsibleListener.onCollapsibleFinished(mIsOpened);
        }

        if (null != mCollapsibleView) {
            mCollapsibleView.setAnimation(null);
        }

        onCollapsibleFinished(mIsOpened);
    }

    /**
     * 伸缩动画
     */
    private class CollapsibleAnimation extends Animation {
        /**
         * 开始的大小
         */
        private int mFromSize;
        /**
         * 结束的大小
         */
        private int mToSize;
        /**
         * 开始的Alpha
         */
        private float mFromAlpha;
        /**
         * 结束的Alpha
         */
        private float mToAlpha;

        /**
         * 构造方法
         *
         * @param fromSize  初始的大小
         * @param toSize    结束的大小
         * @param fromAlpha 初始的透明度
         * @param toAlpha   结束的透明度
         */
        public CollapsibleAnimation(int fromSize, int toSize, float fromAlpha, float toAlpha) {
            mFromSize = fromSize;
            mToSize = toSize;
            mFromAlpha = fromAlpha;
            mToAlpha = toAlpha;
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (mCollapsibleView != null) {
                if (mWithoutAlphaAnim) {
                    // Do nothing
                } else {
                    // 改变透明度
                    final float alpha = mFromAlpha;
                    t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
                }

                // 改变大小
                final int fromSize = mFromSize;
                int size = (int) (fromSize + (mToSize - fromSize) * interpolatedTime);
                setCollapsibleViewSize(size);

                if (null != mCollapsibleListener) {
                    mCollapsibleListener.applyTransformation(mFromSize, mToSize, interpolatedTime);
                }
            }
        }
    }
}