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



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

  1. Android编程之仿微信显示更多文字的View

    微信朋友圈中,如果好友发表的文字过长,会自动收缩起来,底下有提示,当点击“显示更多”时才会展开. 首先定义布局文件(很简单,不解释): <?xml version="1.0" ...

  2. [Android] Android 手机下 仿 微信 客户端 界面 -- 微聊

    Android 手机下 仿 微信 客户端 界面 -- 微聊 (包括聊天列表 + 聊天对话页 + 朋友圈列表页 + 我的/发现 列表页) 项目演示: 功能说明: 1)底部标签切换 (TabHost + ...

  3. 图解android开发在界面上显示图片

    图解android开发在界面上显示图片<申明:转自百度> <原文章地址:http://jingyan.baidu.com/article/49711c6153a277fa441b7c ...

  4. android 随手记 仿微信的popwindow

    /把文字控件添加监听,点击弹出自定义窗口 tv.setOnClickListener(new OnClickListener() { public void onClick(View v) { //实 ...

  5. Android 使用GridView+仿微信图片上传功能(附源代码)

    由于工作要求最近在使用GridView完成图片的批量上传功能,我的例子当中包含仿微信图片上传.拍照.本地选择.相片裁剪等功能,如果有需要的朋友可以看一下,希望我的实际经验能对您有所帮助. 直接上图,下 ...

  6. uniapp+nvue开发之仿微信语音+视频通话功能 :实现一对一语音视频在线通话

    ​ 本篇文章是利用uni-app和nvue实现微信效果功能的第三篇了,今天我们基于uniapp + nvue实现的uniapp仿微信音视频通话插件实例项目,实现了以下功能: 1: 语音通话 2: 视频 ...

  7. 从零开始学android开发- 应用程序窗体显示状态操作requestWindowFeature

    我们在开发程序是经常会需要软件全屏显示.自定义标题(使用按钮等控件)和其他的需求,今天这一讲就是如何控制Android应用程序的窗体显示. 首先介绍一个重要方法那就是requestWindowFeat ...

  8. Android利用ViewPager仿微信主界面-android学习之旅(78)

    首先是介绍ViewPager这个控件 ,这个控件需要pagerAdapter作为容器来提供数据,同时pagerAdapter的数据源是View数组 效果图如下 部分代码如下,实现如下的方法 mPage ...

  9. Android开发:仿美团下拉列表菜单,帮助类,复用简单

    近期在项目中须要用到下拉菜单.公司比較推崇美团的下拉菜单,于是要实现该功能.想着.这个功能应该是一个常常会用到的.于是何不写一个帮助类,仅仅要往这个类里面传入特定的參数,既能够实现下来菜单,并且还能够 ...

随机推荐

  1. 实现兼容document.querySelector的方法

    var querySelector = function(selector) { //TODO 先简单兼容,后续继续扩展: var element = null; if(document.queryS ...

  2. aop难点解析。

    静态织入和动态织入的区别? 需求示例:假设有一个包,一个包当中有一个方法,我们想在这个方法的前后,加上环绕. 那么怎么加呢? 把知道的都说一遍. 1.建立JsonService 2.建立JSONASP ...

  3. linux下硬盘uuid查看及修改设置

    查看硬盘UUID 方法一:ls -l /dev/disk/by-uuid方法二:blkid /dev/sdb1 修改硬盘UUID: uuidgen 会返回一个合法的 uuid,结合 tune2fs 可 ...

  4. ABP官方文档翻译 6.2.1 ASP.NET Core集成

    ASP.NET Core 介绍 迁移到ASP.NET Core? 启动模板 配置 启动类 模块配置 控制器 应用服务作为控制器 过滤器 授权过滤器 审计Action过滤器 校验过滤器 工作单元Acti ...

  5. win7 重装 docker 启动后无法启动错误解决

    描述 win7 重新安装Docker 后启动  Docker Quickstart Terminal 出现如下错误 Starting "default"... (default) ...

  6. 51NOD 1238 最小公倍数之和 V3 [杜教筛]

    1238 最小公倍数之和 V3 三种做法!!! 见学习笔记,这里只贴代码 #include <iostream> #include <cstdio> #include < ...

  7. SPOJ 1812 LCS2 [后缀自动机 DP]

    题意: 求多个串<=10的最长连续子串 一个串建SAM,然后其他串在上面走 每个状态记录所有串在这个状态的公共子串的最小值 一个串在上面走的时候记录与每个状态公共子串的最大值,注意出现次数向父亲 ...

  8. bind,apply,call区别总结

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  9. 升级gitlab

    https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update https://about.gitlab.com/update/#cent ...

  10. Netty基础点滴

    编写一个应答服务器 编写一个应答服务器 写一个Netty服务器主要由两部分组成: 配置服务器功能,如线程.端口 实现服务器处理程序,它包含业务逻辑,决定当有一个请求连接或接收数据时该做什么 启动服务器 ...