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

    最近开发需求中要模仿微信朋友圈文章的展开收起功能,网上找了找,发现都有问题,于是乎自己在前辈的基础上进行了一定量的修改,下边将源码贴出来供大家参考:
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.   
  14. public class MainActivity extends Activity {  

  15. String mStr;  
  16. int type;  
  17.   
  18. @Override  
  19. protected void onCreate(Bundle savedInstanceState) {  
  20. super.onCreate(savedInstanceState);  
  21. setContentView(R.layout.activity_main);  
  22. Globl.map = new HashMap<Integer, Boolean>();  
  23. ListView listview = (ListView) findViewById(R.id.listview);  
  24. mStr = "手指在ListView上下滚动时,ListViewItem背景变黑,因为在滚动的时候为了提升性能做了优化,为提高滚动的性能,Android 框    架在ListView中引入CacheColorHint属性。如果该值为非0,则说明该ListView绘制在单色不透明的背景上,在默认情况下该值 为        #191919,也就是黑色主题中的黑色背景颜色值,这样当ListView滚动的时候";  
  25.     listview.setAdapter(new MyListAdpter(this));  
  26. }  
  27. class MyListAdpter extends BaseAdapter {  
  28. Context con;  
  29. CollapsibleTextView tv;  
  30.   
  31. public MyListAdpter(Context con) {  
  32.     this.con = con;  
  33. }  
  34.   
  35. @Override  
  36. public int getCount() {  
  37.     // TODO Auto-generated method stub  
  38.     return 10;  
  39. }  
  40.   
  41. @Override  
  42. public Object getItem(int position) {  
  43.     // TODO Auto-generated method stub  
  44.     return null;  
  45. }  
  46.   
  47. @Override  
  48. public long getItemId(int position) {  
  49.     // TODO Auto-generated method stub  
  50.     return 0;  
  51. }  
  52.   
  53. HashMap<Integer, View> hashM = new HashMap<Integer, View>();  
  54. @Override  
  55. public View getView(int position, View convertView, ViewGroup parent) {  
  56. Holder holder = null;  
  57. View view;  
  58. if (hashM.get(position) == null) {  
  59.     holder = new Holder();  
  60.     view = LayoutInflater.from(con).inflate(R.layout.item_list, null);  
  61.     holder.tv = (CollapsibleTextView) view.findViewById(R.id.tv_text);  
  62.     holder.tvcount = (TextView) view.findViewById(R.id.tvcount);  
  63.     view.setTag(holder);  
  64.     hashM.put(position, view);  
  65. } else {  
  66.     view = hashM.get(position);  
  67.     holder = (Holder) view.getTag();  
  68. }  
  69.   
  70. // if (Globl.map.get(position) == false) {  
  71. // Globl.map.put(position, false);  
  72. // type = 2;  
  73. // } else {  
  74. // type = 1;  
  75. // }  
  76. // tv.setNowType(type);  
  77. // int typeNow = tv.getNowType();  
  78.     holder.tvcount.setText(position + "");  
  79.     holder.tv.setDesc(mStr, holder.tv, BufferType.NORMAL);  
  80.     return view;  
  81. }  
  82.   
  83. class Holder {  
  84.     CollapsibleTextView tv;  
  85.     TextView tvcount;  
  86. }  
  87. }  
  88.   
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.   
  14.   
  15. private static final int COLLAPSIBLE_STATE_NONE = 0;  
  16. /** View处于展开状态 **/  
  17. private static final int COLLAPSIBLE_STATE_SHRINKUP = 1;  
  18. /** view收缩时状态 **/  
  19. private static final int COLLAPSIBLE_STATE_SPREAD = 2;  
  20. /** 显示内容的View */  
  21. private TextView tv_context;  
  22. /** 展开/收起按钮 */  
  23. private TextView bt_spread;  
  24. private String shrinkup;  
  25. private String spread;  
  26. /** 当前正处于的状态 */  
  27. // private int mState;  
  28. private boolean flag;  
  29. private int nowType;  
  30. private CollapsibleTextView coTextView;  
  31. /** 判断是不是点击了查看更多、收起 */  
  32. private boolean isClicke = false;  
  33. private int lookCount = 0;  
  34.   
  35. public CollapsibleTextView(Context context, AttributeSet attrs) {  
  36. super(context, attrs);  
  37. shrinkup = "收起";  
  38. spread = "查看全文";  
  39. View view = inflate(context, R.layout.collapsible_textview, this);  
  40. view.setPadding(0, -1, 0, 0);  
  41. tv_context = (TextView) view.findViewById(R.id.tv_context);  
  42. bt_spread = (TextView) view.findViewById(R.id.bt_spread);  
  43. bt_spread.setOnClickListener(this);  
  44. }  
  45.   
  46. public CollapsibleTextView(Context context) {  
  47. this(context, null);  
  48. }  
  49.   
  50. /** 
  51. * 赋值 
  52. */  
  53. public final void setDesc(CharSequence charSequence,  
  54. CollapsibleTextView tv, BufferType bufferType) {  
  55. this.coTextView = tv;  
  56. // 对内容中的网址进行处理;  
  57. tv_context.setAutoLinkMask(Linkify.WEB_URLS);  
  58. tv_context.setMovementMethod(LinkMovementMethod.getInstance());  
  59. tv_context.setText(charSequence, bufferType);  
  60. // 初始类型  
  61. if (lookCount == 0) {  
  62. coTextView.setNowType(COLLAPSIBLE_STATE_SPREAD);  
  63. }  
  64. lookCount += 1;  
  65. // TODO LYL 放到ListView中需要加下句:falg=false;一般情况去掉就可  
  66. flag = false;  
  67. requestLayout();  
  68. }  
  69.   
  70.   
  71. @Override  
  72. public void onClick(View v) {  
  73. flag = false;  
  74. isClicke = true;  
  75. requestLayout();  
  76. }  
  77.   
  78.   
  79. @Override  
  80. protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  81. super.onLayout(changed, l, t, r, b);  
  82. if (!flag) {  
  83. flag = true;  
  84. if (tv_context.getLineCount() <= DEFAULT_MAX_LINE_COUNT) {  
  85. bt_spread.setVisibility(View.GONE);  
  86. tv_context.setMaxLines(DEFAULT_MAX_LINE_COUNT + 1);  
  87. coTextView.setNowType(COLLAPSIBLE_STATE_NONE);  
  88. } else {  
  89. post(new InnerRunnable());  
  90.   
  91.   
  92. }  
  93. }  
  94. }  
  95.   
  96.   
  97. class InnerRunnable implements Runnable {  
  98. @Override  
  99. public void run() {  
  100. int zType = 0;  
  101. // 第一次进入操作(没有点击并且是第一次进入);  
  102. System.out.println("lookCount:" + lookCount);  
  103. if (!isClicke && lookCount == 1) {  
  104. if (coTextView.getNowType() == COLLAPSIBLE_STATE_SPREAD) {  
  105. tv_context.setMaxLines(DEFAULT_SHOW_LINE_COUNT);  
  106. bt_spread.setVisibility(View.VISIBLE);  
  107. bt_spread.setText(spread);  
  108. zType = COLLAPSIBLE_STATE_SHRINKUP;  
  109. } else if (coTextView.getNowType() == COLLAPSIBLE_STATE_SHRINKUP) {  
  110. tv_context.setMaxLines(Integer.MAX_VALUE);  
  111. bt_spread.setVisibility(View.VISIBLE);  
  112. bt_spread.setText(shrinkup);  
  113. zType = COLLAPSIBLE_STATE_SPREAD;  
  114. }  
  115. coTextView.setNowType(zType);  
  116. // 点击了查看更多、收起转换状态;  
  117. } else if (isClicke) {  
  118. isClicke = false;  
  119. if (coTextView.getNowType() == COLLAPSIBLE_STATE_SPREAD) {  
  120. tv_context.setMaxLines(DEFAULT_SHOW_LINE_COUNT);  
  121. bt_spread.setVisibility(View.VISIBLE);  
  122. bt_spread.setText(spread);  
  123. coTextView.setNowType(COLLAPSIBLE_STATE_SHRINKUP);  
  124. } else if (coTextView.getNowType() == COLLAPSIBLE_STATE_SHRINKUP) {  
  125. tv_context.setMaxLines(Integer.MAX_VALUE);  
  126. bt_spread.setVisibility(View.VISIBLE);  
  127. bt_spread.setText(shrinkup);  
  128. coTextView.setNowType(COLLAPSIBLE_STATE_SPREAD);  
  129. }  
  130. // 滑动listView 从新载入到可见界面 不做操作,保持原有状态;(为了后面看得人能够更理解写上)  
  131. } else if (!isClicke && lookCount != 1) {  
  132.   
  133. }  
  134.   
  135. }  
  136. }  
  137.   
  138.   
  139. public int getNowType() {  
  140. return nowType;  
  141. }  
  142.   
  143.   
  144. public void setNowType(int nowType) {  
  145. this.nowType = nowType;  
  146. }  
  147.   
  148. }



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);
                }
            }
        }
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值