├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── ExpandableTextView
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── jaydenxiao
│ │ └── com
│ │ └── expandabletextview
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── jaydenxiao
│ │ │ └── com
│ │ │ └── expandabletextview
│ │ │ ├── ExpandableTextView.java
│ │ │ └── UIUtil.java
│ └── res
│ │ ├── drawable-xxhdpi
│ │ ├── icon_green_arrow_down.png
│ │ └── icon_green_arrow_up.png
│ │ ├── layout
│ │ └── item_expand_collapse.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ └── strings.xml
│ └── test
│ └── java
│ └── jaydenxiao
│ └── com
│ └── expandabletextview
│ └── ExampleUnitTest.java
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jaydenxiao
│ │ └── expandabletextview
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── jaydenxiao
│ │ │ └── expandabletextviewexample
│ │ │ ├── ListAdapter.java
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── fra_listview.xml
│ │ ├── fra_scrollview.xml
│ │ └── item_list.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── arrays.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── jaydenxiao
│ └── expandabletextview
│ └── ExampleUnitTest.java
├── arts
├── expandabletextview.gif
└── qrcode_for_gh_e90bfe968c01_430.jpg
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | ExpandableTextViewExample
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ExpandableTextView/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/ExpandableTextView/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | ext{
3 | PUBLISH_GROUP_ID = 'com.jaydenxiao'
4 | PUBLISH_ARTIFACT_ID = 'ExpandableTextView'
5 | PUBLISH_VERSION = '1.0.0'
6 | }
7 | android {
8 | compileSdkVersion 23
9 | buildToolsVersion "24.0.0 rc4"
10 |
11 | defaultConfig {
12 | minSdkVersion 15
13 | targetSdkVersion 23
14 | versionCode 1
15 | versionName "1.0.0"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | //打包不检查
24 | lintOptions {
25 | checkReleaseBuilds false
26 | abortOnError false
27 | }
28 | }
29 |
30 | dependencies {
31 | compile fileTree(dir: 'libs', include: ['*.jar'])
32 | testCompile 'junit:junit:4.12'
33 | compile 'com.android.support:appcompat-v7:23.4.0'
34 | }
35 | apply from: 'https://raw.githubusercontent.com/blundell/release-android-library/master/android-release-aar.gradle'
36 |
--------------------------------------------------------------------------------
/ExpandableTextView/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in H:\android-22\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/ExpandableTextView/src/androidTest/java/jaydenxiao/com/expandabletextview/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package jaydenxiao.com.expandabletextview;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/java/jaydenxiao/com/expandabletextview/ExpandableTextView.java:
--------------------------------------------------------------------------------
1 |
2 | package jaydenxiao.com.expandabletextview;
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.annotation.TargetApi;
6 | import android.content.Context;
7 | import android.content.res.TypedArray;
8 | import android.graphics.drawable.Drawable;
9 | import android.os.Build;
10 | import android.support.v4.content.ContextCompat;
11 | import android.text.TextUtils;
12 | import android.util.AttributeSet;
13 | import android.util.SparseBooleanArray;
14 | import android.view.Gravity;
15 | import android.view.LayoutInflater;
16 | import android.view.MotionEvent;
17 | import android.view.View;
18 | import android.view.ViewGroup;
19 | import android.widget.LinearLayout;
20 | import android.widget.TextView;
21 |
22 | /**
23 | * des:可伸展textview
24 | * Created by xsf
25 | * on 2016.08.24
26 | */
27 | public class ExpandableTextView extends LinearLayout implements View.OnClickListener {
28 |
29 | /* 默认最高行数 */
30 | private static final int MAX_COLLAPSED_LINES = 5;
31 |
32 | /* 默认动画执行时间 */
33 | private static final int DEFAULT_ANIM_DURATION = 200;
34 |
35 | /*内容textview*/
36 | protected TextView mTvContent;
37 |
38 | /*展开收起textview*/
39 | protected TextView mTvExpandCollapse;
40 |
41 | /*是否有重新绘制*/
42 | private boolean mRelayout;
43 |
44 | /*默认收起*/
45 | private boolean mCollapsed = true;
46 |
47 | /*展开图片*/
48 | private Drawable mExpandDrawable;
49 | /*收起图片*/
50 | private Drawable mCollapseDrawable;
51 | /*动画执行时间*/
52 | private int mAnimationDuration;
53 | /*是否正在执行动画*/
54 | private boolean mAnimating;
55 | /* 展开收起状态回调 */
56 | private OnExpandStateChangeListener mListener;
57 | /* listview等列表情况下保存每个item的收起/展开状态 */
58 | private SparseBooleanArray mCollapsedStatus;
59 | /* 列表位置 */
60 | private int mPosition;
61 |
62 | /*设置内容最大行数,超过隐藏*/
63 | private int mMaxCollapsedLines;
64 |
65 | /*这个linerlayout容器的高度*/
66 | private int mCollapsedHeight;
67 |
68 | /*内容tv真实高度(含padding)*/
69 | private int mTextHeightWithMaxLines;
70 |
71 | /*内容tvMarginTopAmndBottom高度*/
72 | private int mMarginBetweenTxtAndBottom;
73 |
74 | /*内容颜色*/
75 | private int contentTextColor;
76 | /*收起展开颜色*/
77 | private int collapseExpandTextColor;
78 | /*内容字体大小*/
79 | private float contentTextSize;
80 | /*收起展字体大小*/
81 | private float collapseExpandTextSize;
82 | /*收起文字*/
83 | private String textCollapse;
84 | /*展开文字*/
85 | private String textExpand;
86 |
87 | /*收起展开位置,默认左边*/
88 | private int grarity;
89 |
90 | /*收起展开图标位置,默认在右边*/
91 | private int drawableGrarity;
92 |
93 | public ExpandableTextView(Context context) {
94 | this(context, null);
95 | }
96 |
97 | public ExpandableTextView(Context context, AttributeSet attrs) {
98 | super(context, attrs);
99 | init(attrs);
100 | }
101 |
102 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
103 | public ExpandableTextView(Context context, AttributeSet attrs, int defStyle) {
104 | super(context, attrs, defStyle);
105 | init(attrs);
106 | }
107 |
108 | @Override
109 | public void setOrientation(int orientation) {
110 | if (LinearLayout.HORIZONTAL == orientation) {
111 | throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation.");
112 | }
113 | super.setOrientation(orientation);
114 | }
115 | /**
116 | * 初始化属性
117 | * @param attrs
118 | */
119 | private void init(AttributeSet attrs) {
120 | mCollapsedStatus=new SparseBooleanArray();
121 |
122 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
123 | mMaxCollapsedLines = typedArray.getInt(R.styleable.ExpandableTextView_maxCollapsedLines, MAX_COLLAPSED_LINES);
124 | mAnimationDuration = typedArray.getInt(R.styleable.ExpandableTextView_animDuration, DEFAULT_ANIM_DURATION);
125 | mExpandDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_expandDrawable);
126 | mCollapseDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_collapseDrawable);
127 |
128 | textCollapse = typedArray.getString(R.styleable.ExpandableTextView_textCollapse);
129 | textExpand = typedArray.getString(R.styleable.ExpandableTextView_textExpand);
130 |
131 | if (mExpandDrawable == null) {
132 | mExpandDrawable = ContextCompat.getDrawable(getContext(),R.drawable.icon_green_arrow_up);
133 | }
134 | if (mCollapseDrawable == null) {
135 | mCollapseDrawable = ContextCompat.getDrawable(getContext(), R.drawable.icon_green_arrow_down);
136 | }
137 |
138 | if (TextUtils.isEmpty(textCollapse)) {
139 | textCollapse = getContext().getString(R.string.collapse);
140 | }
141 | if (TextUtils.isEmpty(textExpand)) {
142 | textExpand = getContext().getString(R.string.expand);
143 | }
144 | contentTextColor = typedArray.getColor(R.styleable.ExpandableTextView_contentTextColor, ContextCompat.getColor(getContext(), R.color.gray));
145 | contentTextSize = typedArray.getDimension(R.styleable.ExpandableTextView_contentTextSize, UIUtil.sp2px(getContext(),14));
146 |
147 | collapseExpandTextColor = typedArray.getColor(R.styleable.ExpandableTextView_collapseExpandTextColor, ContextCompat.getColor(getContext(), R.color.main_color));
148 | collapseExpandTextSize = typedArray.getDimension(R.styleable.ExpandableTextView_collapseExpandTextSize, UIUtil.sp2px(getContext(),14));
149 |
150 | grarity = typedArray.getInt(R.styleable.ExpandableTextView_collapseExpandGrarity, Gravity.LEFT);
151 | drawableGrarity=typedArray.getInt(R.styleable.ExpandableTextView_drawableGrarity, Gravity.RIGHT);
152 |
153 | typedArray.recycle();
154 | // enforces vertical orientation
155 | setOrientation(LinearLayout.VERTICAL);
156 | // default visibility is gone
157 | setVisibility(GONE);
158 | }
159 |
160 | /**
161 | * 渲染完成时初始化view
162 | */
163 | @Override
164 | protected void onFinishInflate() {
165 | findViews();
166 | }
167 |
168 | /**
169 | * 初始化viwe
170 | */
171 | private void findViews() {
172 | LayoutInflater inflater = (LayoutInflater) getContext()
173 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
174 | inflater.inflate(R.layout.item_expand_collapse, this);
175 | mTvContent = (TextView) findViewById(R.id.expandable_text);
176 | mTvContent.setOnClickListener(this);
177 | mTvExpandCollapse = (TextView) findViewById(R.id.expand_collapse);
178 | setDrawbleAndText();
179 | mTvExpandCollapse.setOnClickListener(this);
180 |
181 | mTvContent.setTextColor(contentTextColor);
182 | mTvContent.getPaint().setTextSize(contentTextSize);
183 |
184 | mTvExpandCollapse.setTextColor(collapseExpandTextColor);
185 | mTvExpandCollapse.getPaint().setTextSize(collapseExpandTextSize);
186 |
187 | //设置收起展开位置:左或者右
188 | LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
189 | lp.gravity = grarity;
190 | mTvExpandCollapse.setLayoutParams(lp);
191 | }
192 | /**
193 | * 点击事件
194 | * @param view
195 | */
196 | @Override
197 | public void onClick(View view) {
198 | if (mTvExpandCollapse.getVisibility() != View.VISIBLE) {
199 | return;
200 | }
201 | mCollapsed = !mCollapsed;
202 | //修改收起/展开图标、文字
203 | setDrawbleAndText();
204 | //保存位置状态
205 | if (mCollapsedStatus != null) {
206 | mCollapsedStatus.put(mPosition, mCollapsed);
207 | }
208 | // 执行展开/收起动画
209 | mAnimating = true;
210 | ValueAnimator valueAnimator;
211 | if (mCollapsed) {
212 | // mTvContent.setMaxLines(mMaxCollapsedLines);
213 | valueAnimator = new ValueAnimator().ofInt(getHeight(), mCollapsedHeight);
214 | } else {
215 | valueAnimator = new ValueAnimator().ofInt(getHeight(), getHeight() +
216 | mTextHeightWithMaxLines - mTvContent.getHeight());
217 | }
218 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
219 | @Override
220 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
221 | int animatedValue = (int) valueAnimator.getAnimatedValue();
222 | mTvContent.setMaxHeight(animatedValue - mMarginBetweenTxtAndBottom);
223 | getLayoutParams().height = animatedValue;
224 | requestLayout();
225 | }
226 | });
227 | valueAnimator.addListener(new Animator.AnimatorListener() {
228 | @Override
229 | public void onAnimationStart(Animator animator) {
230 |
231 | }
232 | @Override
233 | public void onAnimationEnd(Animator animator) {
234 | // 动画结束后发送结束的信号
235 | /// clear the animation flag
236 | mAnimating = false;
237 | // notify the listener
238 | if (mListener != null) {
239 | mListener.onExpandStateChanged(mTvContent, !mCollapsed);
240 | }
241 | }
242 | @Override
243 | public void onAnimationCancel(Animator animator) {
244 |
245 | }
246 | @Override
247 | public void onAnimationRepeat(Animator animator) {
248 |
249 | }
250 | });
251 | valueAnimator.setDuration(mAnimationDuration);
252 | valueAnimator.start();
253 | }
254 |
255 | @Override
256 | public boolean onInterceptTouchEvent(MotionEvent ev) {
257 | // 当动画还在执行状态时,拦截事件,不让child处理
258 | return mAnimating;
259 | }
260 | /**
261 | * 重新测量
262 | * @param widthMeasureSpec
263 | * @param heightMeasureSpec
264 | */
265 | @Override
266 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
267 | // If no change, measure and return
268 | if (!mRelayout || getVisibility() == View.GONE) {
269 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
270 | return;
271 | }
272 | mRelayout = false;
273 |
274 | // Setup with optimistic case
275 | // i.e. Everything fits. No button needed
276 | mTvExpandCollapse.setVisibility(View.GONE);
277 | mTvContent.setMaxLines(Integer.MAX_VALUE);
278 |
279 | // Measure
280 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
281 |
282 | //如果内容真实行数小于等于最大行数,不处理
283 | if (mTvContent.getLineCount() <= mMaxCollapsedLines) {
284 | return;
285 | }
286 | // 获取内容tv真实高度(含padding)
287 | mTextHeightWithMaxLines = getRealTextViewHeight(mTvContent);
288 |
289 | // 如果是收起状态,重新设置最大行数
290 | if (mCollapsed) {
291 | mTvContent.setMaxLines(mMaxCollapsedLines);
292 | }
293 | mTvExpandCollapse.setVisibility(View.VISIBLE);
294 |
295 | // Re-measure with new setup
296 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
297 |
298 | if (mCollapsed) {
299 | // Gets the margin between the TextView's bottom and the ViewGroup's bottom
300 | mTvContent.post(new Runnable() {
301 | @Override
302 | public void run() {
303 | mMarginBetweenTxtAndBottom = getHeight() - mTvContent.getHeight();
304 | }
305 | });
306 | // 保存这个容器的测量高度
307 | mCollapsedHeight = getMeasuredHeight();
308 | }
309 | }
310 | /**
311 | * 获取内容tv真实高度(含padding)
312 | * @param textView
313 | * @return
314 | */
315 | private static int getRealTextViewHeight( TextView textView) {
316 | int textHeight = textView.getLayout().getLineTop(textView.getLineCount());
317 | int padding = textView.getCompoundPaddingTop() + textView.getCompoundPaddingBottom();
318 | return textHeight + padding;
319 | }
320 |
321 | /**
322 | * 设置收起展开图标位置和文字
323 | */
324 | private void setDrawbleAndText(){
325 | if(Gravity.LEFT==drawableGrarity){
326 | mTvExpandCollapse.setCompoundDrawablesWithIntrinsicBounds(mCollapsed ? mCollapseDrawable : mExpandDrawable,null,null,null);
327 | }else{
328 | mTvExpandCollapse.setCompoundDrawablesWithIntrinsicBounds(null,null,mCollapsed ? mCollapseDrawable : mExpandDrawable,null);
329 | }
330 | mTvExpandCollapse.setText(mCollapsed ? getResources().getString(R.string.expand) : getResources().getString(R.string.collapse));
331 | }
332 |
333 |
334 | /*********暴露给外部调用方法***********/
335 |
336 | /**
337 | * 设置收起/展开监听
338 | * @param listener
339 | */
340 | public void setOnExpandStateChangeListener( OnExpandStateChangeListener listener) {
341 | mListener = listener;
342 | }
343 |
344 | /**
345 | * 设置内容
346 | * @param text
347 | */
348 | public void setText( CharSequence text) {
349 | mRelayout = true;
350 | mTvContent.setText(text);
351 | setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);
352 | }
353 |
354 | /**
355 | * 设置内容,列表情况下,带有保存位置收起/展开状态
356 | * @param text
357 | * @param position
358 | */
359 | public void setText( CharSequence text,int position) {
360 | mPosition = position;
361 | //获取状态,如无,默认是true:收起
362 | mCollapsed = mCollapsedStatus.get(position, true);
363 | clearAnimation();
364 | //设置收起/展开图标和文字
365 | setDrawbleAndText();
366 | mTvExpandCollapse.setText(mCollapsed ? getResources().getString(R.string.expand) : getResources().getString(R.string.collapse));
367 |
368 | setText(text);
369 | getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
370 | requestLayout();
371 | }
372 |
373 | /**
374 | * 获取内容
375 | * @return
376 | */
377 | public CharSequence getText() {
378 | if (mTvContent == null) {
379 | return "";
380 | }
381 | return mTvContent.getText();
382 | }
383 |
384 | /**
385 | * 定义状态改变接口
386 | */
387 | public interface OnExpandStateChangeListener {
388 | /**
389 | * @param textView - TextView being expanded/collapsed
390 | * @param isExpanded - true if the TextView has been expanded
391 | */
392 | void onExpandStateChanged(TextView textView, boolean isExpanded);
393 | }
394 | }
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/java/jaydenxiao/com/expandabletextview/UIUtil.java:
--------------------------------------------------------------------------------
1 | package jaydenxiao.com.expandabletextview;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.view.View;
6 | import android.view.ViewTreeObserver;
7 | import android.view.ViewTreeObserver.OnGlobalLayoutListener;
8 | import android.view.WindowManager;
9 | import android.widget.LinearLayout;
10 |
11 | /**
12 | * des:屏幕相关辅助类
13 | * Created by xsf
14 | * on 2016.08.24
15 | */
16 | public class UIUtil {
17 | private UIUtil() {
18 | /* cannot be instantiated */
19 | throw new UnsupportedOperationException("cannot be instantiated");
20 | }
21 |
22 | /**
23 | * 将px值转换为dip或dp值,保证尺寸大小不变
24 | *
25 | * @param pxValue
26 | * (DisplayMetrics类中属性density)
27 | * @return
28 | */
29 | public static int px2dip(Context context,float pxValue) {
30 | final float scale = context.getResources().getDisplayMetrics().density;
31 | return (int) (pxValue / scale + 0.5f);
32 | }
33 |
34 | /**
35 | * 将dip或dp值转换为px值,保证尺寸大小不变
36 | *
37 | * @param dipValue
38 | * @return
39 | */
40 | public static int dip2px( Context context ,float dipValue) {
41 | final float scale = context.getResources().getDisplayMetrics().density;
42 | return (int) (dipValue * scale + 0.5f);
43 | }
44 |
45 | /**
46 | * 将px值转换为sp值,保证文字大小不变
47 | *
48 | * @param pxValue
49 | * @return
50 | */
51 | public static int px2sp(Context context,float pxValue) {
52 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
53 | return (int) (pxValue / fontScale + 0.5f);
54 | }
55 |
56 | /**
57 | * 将sp值转换为px值,保证文字大小不变
58 | *
59 | * @param spValue
60 | * @return
61 | */
62 | public static int sp2px(Context context,float spValue) {
63 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
64 | return (int) (spValue * fontScale + 0.5f);
65 | }
66 |
67 | /**
68 | * 直接获取控件的宽、高
69 | * @param view
70 | * @return int[]
71 | */
72 | public static int[] getWidgetWH(final View view){
73 | ViewTreeObserver vto2 = view.getViewTreeObserver();
74 | vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
75 | @Override
76 | public void onGlobalLayout() {
77 | view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
78 | }
79 | });
80 | return new int[]{view.getWidth(),view.getHeight()};
81 | }
82 |
83 | /**
84 | * 直接获取控件的宽、高
85 | * @param view
86 | * @return int[]
87 | */
88 | public static int getViewHeight(final View view){
89 | ViewTreeObserver vto2 = view.getViewTreeObserver();
90 | vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
91 | @Override
92 | public void onGlobalLayout() {
93 | view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
94 | }
95 | });
96 | return view.getHeight();
97 | }
98 |
99 | /**
100 | * 直接获取控件的宽、高
101 | * @param view
102 | * @return int[]
103 | */
104 | public static int getViewWidth(final View view){
105 | ViewTreeObserver vto2 = view.getViewTreeObserver();
106 | vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
107 | @Override
108 | public void onGlobalLayout() {
109 | view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
110 | }
111 | });
112 | return view.getWidth();
113 | }
114 |
115 | /**
116 | * 获得屏幕宽度
117 | *
118 | * @param context
119 | * @return
120 | */
121 | public static int getScreenWidth(Context context) {
122 | WindowManager wm = (WindowManager) context
123 | .getSystemService(Context.WINDOW_SERVICE);
124 | DisplayMetrics outMetrics = new DisplayMetrics();
125 | wm.getDefaultDisplay().getMetrics(outMetrics);
126 | return outMetrics.widthPixels;
127 | }
128 |
129 | /**
130 | * 获得屏幕高度
131 | *
132 | * @param context
133 | * @return
134 | */
135 | public static int getScreenHeight(Context context) {
136 | WindowManager wm = (WindowManager) context
137 | .getSystemService(Context.WINDOW_SERVICE);
138 | DisplayMetrics outMetrics = new DisplayMetrics();
139 | wm.getDefaultDisplay().getMetrics(outMetrics);
140 | return outMetrics.heightPixels;
141 | }
142 |
143 | /**
144 | * 获取控件的宽
145 | * @param view
146 | * @return
147 | */
148 | public static int getWidgetWidth(View view){
149 | int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
150 | int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
151 | view.measure(w, h);//先度量
152 | int width = view.getMeasuredWidth();
153 | return width;
154 | }
155 | /**
156 | * 获取控件的高
157 | * @param view
158 | * @return
159 | */
160 | public static int getWidgetHeight(View view){
161 | int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
162 | int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
163 | view.measure(w, h);//先度量
164 | int height = view.getMeasuredHeight();
165 | return height;
166 | }
167 | /**
168 | * 设置控件宽
169 | * @param view
170 | * @param width
171 | */
172 | public static void setWidgetWidth(View view, int width){
173 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
174 | params.width = width;
175 | view.setLayoutParams(params);
176 | }
177 | /**
178 | * 设置控件高
179 | * @param view
180 | * @param height
181 | */
182 | public static void setWidgetHeight(View view, int height){
183 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
184 | params.height = height;
185 | view.setLayoutParams(params);
186 | }
187 | //获得状态栏的高度
188 | public static int getStatusBarHeight(Context context) {
189 | int result = 0;
190 | int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
191 | if (resId > 0) {
192 | result = context.getResources().getDimensionPixelOffset(resId);
193 | }
194 | return result;
195 | }
196 |
197 | }
198 |
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/res/drawable-xxhdpi/icon_green_arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/ExpandableTextView/src/main/res/drawable-xxhdpi/icon_green_arrow_down.png
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/res/drawable-xxhdpi/icon_green_arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/ExpandableTextView/src/main/res/drawable-xxhdpi/icon_green_arrow_up.png
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/res/layout/item_expand_collapse.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
16 |
30 |
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FA7C20
4 | #333333
5 |
6 |
--------------------------------------------------------------------------------
/ExpandableTextView/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ExpandableTextView
3 | 展开
4 | 收起
5 | The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recently married each other has gone viral, southcn.com reported.
6 | Zhou Si, the groom, and Chen Mengge, the bride, married each other on Aug 18 and will together go to Oxford University to pursue their doctoral degrees later this year.
7 | They were deskmate in middle school and then both were admitted by same high school but not in same class.
8 |
9 |
--------------------------------------------------------------------------------
/ExpandableTextView/src/test/java/jaydenxiao/com/expandabletextview/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package jaydenxiao.com.expandabletextview;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ###非常轻量的可展开和收缩内容的TextView,可用于listview等列表或普通布局情况
2 | ###项目介绍:
3 | 一个支持可展开和收缩内容的TextView,支持在listview等列表使用不错乱,支持在普通局部使用
4 | ###国际案例:有图有真相
5 | 
6 | ###支持配置属性:
7 | - 显示内容最大显示行数配置
8 | - 显示内容字体大小颜色配置
9 | - 展开/收起图标配置
10 | - 展开/收起位置配置
11 | - 展开/收起字体大小颜色配置
12 | - 展开/收起变化监听
13 |
14 | ###使用方法
15 | - use Gradle:
16 |
17 | ```javascript
18 | dependencies {
19 |
20 | compile 'com.jaydenxiao:ExpandableTextView:1.0.0'
21 |
22 | }
23 | ```
24 | - Or Maven:
25 |
26 | ```javascript
27 |
28 | com.jaydenxiao
29 | ExpandableTextView
30 | 1.0.0
31 | pom
32 |
33 | ```
34 |
35 | - Or download the libray for your module:
36 |
37 |
38 | ######布局文件
39 | ```javascript
40 |
55 | ```
56 |
57 | ######设置text值
58 |
59 | - 普通情况
60 | expandableTextView.setText("content");
61 | - 列表情况(把item位置传入,记录状态)
62 | expandableTextView.setText("content",position);
63 |
64 |
65 | **更多精彩文章请关注微信公众号"Android经验分享":这里将长期为您分享Android高手经验、中外开源项目、源码解析、框架设计和Android好文推荐!**
66 |
67 |
68 | 
69 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "24.0.0 rc4"
6 |
7 | defaultConfig {
8 | applicationId "com.jaydenxiao.expandabletextview"
9 | minSdkVersion 15
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:23.4.0'
26 | compile "com.android.support:design:23.4.0"
27 | compile project(':ExpandableTextView')
28 | }
29 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in H:\android-22\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/jaydenxiao/expandabletextview/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.expandabletextview;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/expandabletextviewexample/ListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.expandabletextviewexample;
2 |
3 | import android.content.Context;
4 | import android.util.SparseBooleanArray;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.BaseAdapter;
9 |
10 | import jaydenxiao.com.expandabletextview.ExpandableTextView;
11 |
12 | /**
13 | * des:适配器
14 | * Created by xsf
15 | * on 2016.08.24
16 | */
17 | public class ListAdapter extends BaseAdapter {
18 |
19 | private final Context mContext;
20 | private final SparseBooleanArray mCollapsedStatus;
21 | private final String[] sampleStrings;
22 |
23 | public ListAdapter(Context context) {
24 | mContext = context;
25 | mCollapsedStatus = new SparseBooleanArray();
26 | sampleStrings = mContext.getResources().getStringArray(R.array.exampleStrings);
27 | }
28 |
29 | @Override
30 | public int getCount() {
31 | return sampleStrings.length;
32 | }
33 |
34 | @Override
35 | public Object getItem(int position) {
36 | return null;
37 | }
38 |
39 | @Override
40 | public long getItemId(int position) {
41 | return 0;
42 | }
43 |
44 | @Override
45 | public View getView(int position, View convertView, ViewGroup parent) {
46 | final ViewHolder viewHolder;
47 | if (convertView == null) {
48 | convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list, parent, false);
49 | viewHolder = new ViewHolder();
50 | viewHolder.expandableTextView = (ExpandableTextView) convertView.findViewById(R.id.etv);
51 | convertView.setTag(viewHolder);
52 | } else {
53 | viewHolder = (ViewHolder) convertView.getTag();
54 | }
55 | viewHolder.expandableTextView.setText(sampleStrings[position], position);
56 | return convertView;
57 | }
58 |
59 |
60 | private static class ViewHolder{
61 | ExpandableTextView expandableTextView;
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/expandabletextviewexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.expandabletextviewexample;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.app.FragmentManager;
7 | import android.support.v4.app.FragmentPagerAdapter;
8 | import android.support.v4.app.ListFragment;
9 | import android.support.v4.view.ViewPager;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.TextView;
15 | import android.widget.Toast;
16 |
17 | import jaydenxiao.com.expandabletextview.ExpandableTextView;
18 |
19 |
20 | public class MainActivity extends AppCompatActivity {
21 | private ViewPager mViewPager;
22 | private TabLayout mTabLayout;
23 | private SectionsPagerAdapter mSectionsPagerAdapter;
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_main);
28 | initiView();
29 | initData();
30 | }
31 |
32 | private void initiView() {
33 | mViewPager= (ViewPager) findViewById(R.id.container);
34 | mTabLayout= (TabLayout) findViewById(R.id.tabs );
35 | }
36 |
37 | private void initData() {
38 | mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
39 | mViewPager = (ViewPager) findViewById(R.id.container);
40 | mViewPager.setAdapter(mSectionsPagerAdapter);
41 |
42 | mTabLayout = (TabLayout) findViewById(R.id.tabs);
43 | mTabLayout.setupWithViewPager(mViewPager);
44 | setupTabLayout(mTabLayout);
45 | }
46 |
47 |
48 | private void setupTabLayout(TabLayout tabLayout) {
49 | tabLayout.setTabMode(TabLayout.MODE_FIXED);
50 | tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
51 | tabLayout.setupWithViewPager(mViewPager);
52 | }
53 | /**
54 | * viewpager适配器
55 | */
56 | class SectionsPagerAdapter extends FragmentPagerAdapter {
57 |
58 | public SectionsPagerAdapter(FragmentManager fm) {
59 | super(fm);
60 | }
61 |
62 | @Override
63 | public Fragment getItem(int position) {
64 | if (position == 0) {
65 | return new FragmentListview();
66 | } else {
67 | return new FragemntScrollview();
68 | }
69 | }
70 |
71 | @Override
72 | public int getCount() {
73 | return 2;
74 | }
75 |
76 | @Override
77 | public CharSequence getPageTitle(int position) {
78 | switch (position) {
79 | case 0:
80 | return getString(R.string.situation1);
81 | case 1:
82 | return getString(R.string.situation2);
83 | }
84 | return null;
85 | }
86 | }
87 |
88 | /**
89 | 非列表情况fragment
90 | */
91 | public static class FragemntScrollview extends Fragment {
92 | @Override
93 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
94 | Bundle savedInstanceState) {
95 | View rootView = inflater.inflate(R.layout.fra_scrollview, container, false);
96 |
97 | ExpandableTextView etvLeft = (ExpandableTextView) rootView.findViewById(R.id.etv_left);
98 | ExpandableTextView etvpRight = (ExpandableTextView) rootView.findViewById(R.id.etv_right);
99 |
100 | etvLeft.setOnExpandStateChangeListener(new ExpandableTextView.OnExpandStateChangeListener() {
101 | @Override
102 | public void onExpandStateChanged(TextView textView, boolean isExpanded) {
103 | Toast.makeText(getActivity(), isExpanded ? "展开" : "收起", Toast.LENGTH_SHORT).show();
104 | }
105 | });
106 | etvpRight.setOnExpandStateChangeListener(new ExpandableTextView.OnExpandStateChangeListener() {
107 | @Override
108 | public void onExpandStateChanged(TextView textView, boolean isExpanded) {
109 | Toast.makeText(getActivity(), isExpanded ? "展开" : "收起", Toast.LENGTH_SHORT).show();
110 | }
111 | });
112 | etvLeft.setText(getString(R.string.test_content));
113 | etvpRight.setText(getString(R.string.test_content));
114 |
115 | return rootView;
116 | }
117 | }
118 | /**
119 | 列表情况fragment
120 | */
121 | public static class FragmentListview extends ListFragment {
122 | @Override
123 | public void onViewCreated(View view, Bundle savedInstanceState) {
124 | super.onViewCreated(view, savedInstanceState);
125 | ListAdapter adapter = new ListAdapter(getActivity());
126 | setListAdapter(adapter);
127 | }
128 | }
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fra_listview.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fra_scrollview.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
17 |
18 |
26 |
40 |
48 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - "The story of a Chinese couple at Oxford University who first met eay who first met each other as deskmate in middle school and recently married each other has gone viral, ch other as deskmate in middle school and recently The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recentlmarried each other has gone viral, southcn.com reported. how to build apps using Android's various APIs."
5 | - "The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recently married each other has gone viral, southcn.com reported., and a service independently performs work The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recentlin the background."
6 | - "The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recently married each other has gone viral, southcn.com reported.ty in a maps app to show an address. This model provides multiple entry points for a single app and allows any app to behave as a user's "default" The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recentlfor an action that other apps may invoke."
7 | - "The story of a Chinese couple at Oxford University who first met each other as deskmate y who first met each other as deskmate in middle school and recently married each other has gone viral, in middle school and recently married each other has gone viral, southcn.com reported.ardware such as a camera. If necessary, you can also declare features your app requires so app markets such as Google Play Store do not allow installation on devices that do not support that feature."
8 | - "The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recently married each other has gone viral, southcn.com reported.s. For example, you can create different XML layout files for different screen sizes and the system determines which layout to apply based on the current device's screen size."
9 | - "Android provides a rich apThe story of a Chinese couple at Oxford University who first met each other as dey who first met each other as deskmate in middle school and recently married each other has gone viral, skmate in middle school and recentlplication framework that allows you to build innovative apps and games for mobile devices in a Java language environment. The documents listed in the left navigation provide details about how to build apps using Android's various APIs."
10 | - "Android provides a rich application ThThe story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recentle story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recently married each other has gone viral, southcn.com reported."
11 | - "Android provides a rich application framework that The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recentlallows you to build innovative apps and games for mobile devices in a Java language environment. The documents listed in the left navigation provide details about how to build apps using Android's various APIs."
12 | - "The story of a Chinese couple at Oxford University who first met The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recentleach other as deskmate in middle school and recently married each other has gone viral, southcn.com reported.uments listed in the left navigation provide details about how to build apps using Android's various APIs."
13 | - "Android provides a rich application framework that allows you to build innovative apps and games for mobile devices in a Java language environment. The documents listed in the left navigation provide details about how to build apps using Android's various APIs."
14 | - "test application The story"
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FA7C20
4 | #FA7C20
5 | #FA7C20
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ExpandableTextView
3 | 非列表情况
4 | 列表情况
5 | The story of a Chinese couple at Oxford University who first met each other as deskmate in middle school and recently married each other has gone viral, southcn.com reported.
6 | Zhou Si, the groom, and Chen Mengge, the bride, married each other on Aug 18 and will together go to Oxford University to pursue their doctoral degrees later this year.
7 | They were deskmate in middle school and then both were admitted by same high school but not in same class.
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/test/java/com/jaydenxiao/expandabletextview/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.expandabletextview;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/arts/expandabletextview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/arts/expandabletextview.gif
--------------------------------------------------------------------------------
/arts/qrcode_for_gh_e90bfe968c01_430.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/arts/qrcode_for_gh_e90bfe968c01_430.jpg
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.0.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/ExpandableTextViewExample/f387755f12a2296a787754115b70d32a3bb0b832/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':ExpandableTextView'
2 |
--------------------------------------------------------------------------------