items) {
24 | mValues = items;
25 | }
26 |
27 | @Override
28 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
29 | View view = LayoutInflater.from(parent.getContext())
30 | .inflate(R.layout.fragment_item, parent, false);
31 | return new ViewHolder(view);
32 | }
33 |
34 | @Override
35 | public void onBindViewHolder(final ViewHolder holder, int position) {
36 | holder.mItem = mValues.get(position);
37 | holder.mIdView.setText(mValues.get(position).id);
38 | holder.mContentView.setText(mValues.get(position).content);
39 | }
40 |
41 | @Override
42 | public int getItemCount() {
43 | return mValues.size();
44 | }
45 |
46 | public class ViewHolder extends RecyclerView.ViewHolder {
47 | public final View mView;
48 | public final TextView mIdView;
49 | public final TextView mContentView;
50 | public DummyItem mItem;
51 |
52 | public ViewHolder(View view) {
53 | super(view);
54 | mView = view;
55 | mIdView = (TextView) view.findViewById(R.id.id);
56 | mContentView = (TextView) view.findViewById(R.id.content);
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return super.toString() + " '" + mContentView.getText() + "'";
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/me/stefan/easybehavior/fragment/dummy/DummyContent.java:
--------------------------------------------------------------------------------
1 | package me.stefan.easybehavior.fragment.dummy;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * Helper class for providing sample content for user interfaces created by
10 | * Android template wizards.
11 | *
12 | * TODO: Replace all uses of this class before publishing your app.
13 | */
14 | public class DummyContent {
15 |
16 | /**
17 | * An array of sample (dummy) items.
18 | */
19 | public static final List ITEMS = new ArrayList();
20 |
21 | /**
22 | * A map of sample (dummy) items, by ID.
23 | */
24 | public static final Map ITEM_MAP = new HashMap();
25 |
26 | private static final int COUNT = 25;
27 |
28 | static {
29 | // Add some sample items.
30 | for (int i = 1; i <= COUNT; i++) {
31 | addItem(createDummyItem(i));
32 | }
33 | }
34 |
35 | private static void addItem(DummyItem item) {
36 | ITEMS.add(item);
37 | ITEM_MAP.put(item.id, item);
38 | }
39 |
40 | private static DummyItem createDummyItem(int position) {
41 | return new DummyItem(String.valueOf(position), "Item " + position, makeDetails(position));
42 | }
43 |
44 | private static String makeDetails(int position) {
45 | StringBuilder builder = new StringBuilder();
46 | builder.append("Details about Item: ").append(position);
47 | for (int i = 0; i < position; i++) {
48 | builder.append("\nMore details information here.");
49 | }
50 | return builder.toString();
51 | }
52 |
53 | /**
54 | * A dummy item representing a piece of content.
55 | */
56 | public static class DummyItem {
57 | public final String id;
58 | public final String content;
59 | public final String details;
60 |
61 | public DummyItem(String id, String content, String details) {
62 | this.id = id;
63 | this.content = content;
64 | this.details = details;
65 | }
66 |
67 | @Override
68 | public String toString() {
69 | return content;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/me/stefan/easybehavior/fragment/dummy/TabEntity.java:
--------------------------------------------------------------------------------
1 | package me.stefan.easybehavior.fragment.dummy;
2 |
3 | import com.flyco.tablayout.listener.CustomTabEntity;
4 |
5 | public class TabEntity implements CustomTabEntity {
6 | public String title;
7 | public int selectedIcon;
8 | public int unSelectedIcon;
9 | public String subTitle;
10 |
11 | public TabEntity(String title, int selectedIcon, int unSelectedIcon) {
12 | this.title = title;
13 | this.selectedIcon = selectedIcon;
14 | this.unSelectedIcon = unSelectedIcon;
15 | }
16 |
17 | public TabEntity(String title, String subTitle) {
18 | this.title = title;
19 | this.subTitle = subTitle;
20 | }
21 |
22 | @Override
23 | public String getTabTitle() {
24 | return title;
25 | }
26 |
27 | @Override
28 | public int getTabSelectedIcon() {
29 | return selectedIcon;
30 | }
31 |
32 | @Override
33 | public int getTabUnselectedIcon() {
34 | return unSelectedIcon;
35 | }
36 |
37 | @Override
38 | public String getSubTitle() {
39 | return subTitle;
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/stefan/easybehavior/widget/CircleImageView.java:
--------------------------------------------------------------------------------
1 | package me.stefan.easybehavior.widget;
2 | import android.content.Context;
3 | import android.content.res.TypedArray;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapShader;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.ColorFilter;
9 | import android.graphics.Matrix;
10 | import android.graphics.Paint;
11 | import android.graphics.RectF;
12 | import android.graphics.Shader;
13 | import android.graphics.drawable.BitmapDrawable;
14 | import android.graphics.drawable.ColorDrawable;
15 | import android.graphics.drawable.Drawable;
16 | import android.net.Uri;
17 | import android.support.annotation.ColorInt;
18 | import android.support.annotation.ColorRes;
19 | import android.support.annotation.DrawableRes;
20 | import android.support.v7.widget.AppCompatImageView;
21 | import android.util.AttributeSet;
22 | import android.widget.ImageView;
23 |
24 | import me.stefan.easybehavior.R;
25 |
26 |
27 | /**
28 | * Created by wxy on 16/6/27.
29 | */
30 | public class CircleImageView extends ImageView {
31 | private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
32 |
33 | private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
34 | private static final int COLORDRAWABLE_DIMENSION = 2;
35 |
36 | private static final int DEFAULT_BORDER_WIDTH = 0;
37 | private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
38 | private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
39 | private static final boolean DEFAULT_BORDER_OVERLAY = false;
40 |
41 | private final RectF mDrawableRect = new RectF();
42 | private final RectF mBorderRect = new RectF();
43 |
44 | private final Matrix mShaderMatrix = new Matrix();
45 | private final Paint mBitmapPaint = new Paint();
46 | private final Paint mBorderPaint = new Paint();
47 | private final Paint mFillPaint = new Paint();
48 |
49 | private int mBorderColor = DEFAULT_BORDER_COLOR;
50 | private int mBorderWidth = DEFAULT_BORDER_WIDTH;
51 | private int mFillColor = DEFAULT_FILL_COLOR;
52 |
53 | private Bitmap mBitmap;
54 | private BitmapShader mBitmapShader;
55 | private int mBitmapWidth;
56 | private int mBitmapHeight;
57 |
58 | private float mDrawableRadius;
59 | private float mBorderRadius;
60 |
61 | private ColorFilter mColorFilter;
62 |
63 | private boolean mReady;
64 | private boolean mSetupPending;
65 | private boolean mBorderOverlay;
66 | private boolean mDisableCircularTransformation;
67 |
68 | public CircleImageView(Context context) {
69 | super(context);
70 |
71 | init();
72 | }
73 |
74 | public CircleImageView(Context context, AttributeSet attrs) {
75 | this(context, attrs, 0);
76 | }
77 |
78 | public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
79 | super(context, attrs, defStyle);
80 |
81 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
82 |
83 | mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
84 | mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
85 | mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
86 | mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);
87 |
88 | a.recycle();
89 |
90 | init();
91 | }
92 |
93 | private void init() {
94 | super.setScaleType(SCALE_TYPE);
95 | mReady = true;
96 |
97 | if (mSetupPending) {
98 | setup();
99 | mSetupPending = false;
100 | }
101 | }
102 |
103 | @Override
104 | public ScaleType getScaleType() {
105 | return SCALE_TYPE;
106 | }
107 |
108 | @Override
109 | public void setScaleType(ScaleType scaleType) {
110 | if (scaleType != SCALE_TYPE) {
111 | throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
112 | }
113 | }
114 |
115 | @Override
116 | public void setAdjustViewBounds(boolean adjustViewBounds) {
117 | if (adjustViewBounds) {
118 | throw new IllegalArgumentException("adjustViewBounds not supported.");
119 | }
120 | }
121 |
122 | @Override
123 | protected void onDraw(Canvas canvas) {
124 | if (mDisableCircularTransformation) {
125 | super.onDraw(canvas);
126 | return;
127 | }
128 |
129 | if (mBitmap == null) {
130 | return;
131 | }
132 |
133 | if (mFillColor != Color.TRANSPARENT) {
134 | canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint);
135 | }
136 | canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
137 | if (mBorderWidth > 0) {
138 | canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
139 | }
140 | }
141 |
142 | @Override
143 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
144 | super.onSizeChanged(w, h, oldw, oldh);
145 | setup();
146 | }
147 |
148 | public int getBorderColor() {
149 | return mBorderColor;
150 | }
151 |
152 | public void setBorderColor(@ColorInt int borderColor) {
153 | if (borderColor == mBorderColor) {
154 | return;
155 | }
156 |
157 | mBorderColor = borderColor;
158 | mBorderPaint.setColor(mBorderColor);
159 | invalidate();
160 | }
161 |
162 | /**
163 | * @deprecated Use {@link #setBorderColor(int)} instead
164 | */
165 | @Deprecated
166 | public void setBorderColorResource(@ColorRes int borderColorRes) {
167 | setBorderColor(getContext().getResources().getColor(borderColorRes));
168 | }
169 |
170 | /**
171 | * Return the color drawn behind the circle-shaped drawable.
172 | *
173 | * @return The color drawn behind the drawable
174 | *
175 | * @deprecated Fill color support is going to be removed in the future
176 | */
177 | @Deprecated
178 | public int getFillColor() {
179 | return mFillColor;
180 | }
181 |
182 | /**
183 | * Set a color to be drawn behind the circle-shaped drawable. Note that
184 | * this has no effect if the drawable is opaque or no drawable is set.
185 | *
186 | * @param fillColor The color to be drawn behind the drawable
187 | *
188 | * @deprecated Fill color support is going to be removed in the future
189 | */
190 | @Deprecated
191 | public void setFillColor(@ColorInt int fillColor) {
192 | if (fillColor == mFillColor) {
193 | return;
194 | }
195 |
196 | mFillColor = fillColor;
197 | mFillPaint.setColor(fillColor);
198 | invalidate();
199 | }
200 |
201 | /**
202 | * Set a color to be drawn behind the circle-shaped drawable. Note that
203 | * this has no effect if the drawable is opaque or no drawable is set.
204 | *
205 | * @param fillColorRes The color resource to be resolved to a color and
206 | * drawn behind the drawable
207 | *
208 | * @deprecated Fill color support is going to be removed in the future
209 | */
210 | @Deprecated
211 | public void setFillColorResource(@ColorRes int fillColorRes) {
212 | setFillColor(getContext().getResources().getColor(fillColorRes));
213 | }
214 |
215 | public int getBorderWidth() {
216 | return mBorderWidth;
217 | }
218 |
219 | public void setBorderWidth(int borderWidth) {
220 | if (borderWidth == mBorderWidth) {
221 | return;
222 | }
223 |
224 | mBorderWidth = borderWidth;
225 | setup();
226 | }
227 |
228 | public boolean isBorderOverlay() {
229 | return mBorderOverlay;
230 | }
231 |
232 | public void setBorderOverlay(boolean borderOverlay) {
233 | if (borderOverlay == mBorderOverlay) {
234 | return;
235 | }
236 |
237 | mBorderOverlay = borderOverlay;
238 | setup();
239 | }
240 |
241 | public boolean isDisableCircularTransformation() {
242 | return mDisableCircularTransformation;
243 | }
244 |
245 | public void setDisableCircularTransformation(boolean disableCircularTransformation) {
246 | if (mDisableCircularTransformation == disableCircularTransformation) {
247 | return;
248 | }
249 |
250 | mDisableCircularTransformation = disableCircularTransformation;
251 | initializeBitmap();
252 | }
253 |
254 | @Override
255 | public void setImageBitmap(Bitmap bm) {
256 | super.setImageBitmap(bm);
257 | initializeBitmap();
258 | }
259 |
260 | @Override
261 | public void setImageDrawable(Drawable drawable) {
262 | super.setImageDrawable(drawable);
263 | initializeBitmap();
264 | }
265 |
266 | @Override
267 | public void setImageResource(@DrawableRes int resId) {
268 | super.setImageResource(resId);
269 | initializeBitmap();
270 | }
271 |
272 | @Override
273 | public void setImageURI(Uri uri) {
274 | super.setImageURI(uri);
275 | initializeBitmap();
276 | }
277 |
278 | @Override
279 | public void setColorFilter(ColorFilter cf) {
280 | if (cf == mColorFilter) {
281 | return;
282 | }
283 |
284 | mColorFilter = cf;
285 | applyColorFilter();
286 | invalidate();
287 | }
288 |
289 | @Override
290 | public ColorFilter getColorFilter() {
291 | return mColorFilter;
292 | }
293 |
294 | private void applyColorFilter() {
295 | if (mBitmapPaint != null) {
296 | mBitmapPaint.setColorFilter(mColorFilter);
297 | }
298 | }
299 |
300 | private Bitmap getBitmapFromDrawable(Drawable drawable) {
301 | if (drawable == null) {
302 | return null;
303 | }
304 |
305 | if (drawable instanceof BitmapDrawable) {
306 | return ((BitmapDrawable) drawable).getBitmap();
307 | }
308 |
309 | try {
310 | Bitmap bitmap;
311 |
312 | if (drawable instanceof ColorDrawable) {
313 | bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
314 | } else {
315 | bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
316 | }
317 |
318 | Canvas canvas = new Canvas(bitmap);
319 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
320 | drawable.draw(canvas);
321 | return bitmap;
322 | } catch (Exception e) {
323 | e.printStackTrace();
324 | return null;
325 | }
326 | }
327 |
328 | private void initializeBitmap() {
329 | if (mDisableCircularTransformation) {
330 | mBitmap = null;
331 | } else {
332 | mBitmap = getBitmapFromDrawable(getDrawable());
333 | }
334 | setup();
335 | }
336 |
337 | private void setup() {
338 | if (!mReady) {
339 | mSetupPending = true;
340 | return;
341 | }
342 |
343 | if (getWidth() == 0 && getHeight() == 0) {
344 | return;
345 | }
346 |
347 | if (mBitmap == null) {
348 | invalidate();
349 | return;
350 | }
351 |
352 | mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
353 |
354 | mBitmapPaint.setAntiAlias(true);
355 | mBitmapPaint.setShader(mBitmapShader);
356 |
357 | mBorderPaint.setStyle(Paint.Style.STROKE);
358 | mBorderPaint.setAntiAlias(true);
359 | mBorderPaint.setColor(mBorderColor);
360 | mBorderPaint.setStrokeWidth(mBorderWidth);
361 |
362 | mFillPaint.setStyle(Paint.Style.FILL);
363 | mFillPaint.setAntiAlias(true);
364 | mFillPaint.setColor(mFillColor);
365 |
366 | mBitmapHeight = mBitmap.getHeight();
367 | mBitmapWidth = mBitmap.getWidth();
368 |
369 | mBorderRect.set(calculateBounds());
370 | mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
371 |
372 | mDrawableRect.set(mBorderRect);
373 | if (!mBorderOverlay && mBorderWidth > 0) {
374 | mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
375 | }
376 | mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
377 |
378 | applyColorFilter();
379 | updateShaderMatrix();
380 | invalidate();
381 | }
382 |
383 | private RectF calculateBounds() {
384 | int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();
385 | int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
386 |
387 | int sideLength = Math.min(availableWidth, availableHeight);
388 |
389 | float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
390 | float top = getPaddingTop() + (availableHeight - sideLength) / 2f;
391 |
392 | return new RectF(left, top, left + sideLength, top + sideLength);
393 | }
394 |
395 | private void updateShaderMatrix() {
396 | float scale;
397 | float dx = 0;
398 | float dy = 0;
399 |
400 | mShaderMatrix.set(null);
401 |
402 | if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
403 | scale = mDrawableRect.height() / (float) mBitmapHeight;
404 | dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
405 | } else {
406 | scale = mDrawableRect.width() / (float) mBitmapWidth;
407 | dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
408 | }
409 |
410 | mShaderMatrix.setScale(scale, scale);
411 | mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
412 | mBitmapShader.setLocalMatrix(mShaderMatrix);
413 | }
414 |
415 | }
416 |
--------------------------------------------------------------------------------
/app/src/main/java/me/stefan/easybehavior/widget/DisInterceptNestedScrollView.java:
--------------------------------------------------------------------------------
1 | package me.stefan.easybehavior.widget;
2 |
3 | import android.content.Context;
4 | import android.support.v4.widget.NestedScrollView;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 |
8 | /**
9 | * Created by stefan on 2017/5/26.
10 | * Func:用于子类防止父类拦截子类的事件
11 | */
12 | public class DisInterceptNestedScrollView extends NestedScrollView {
13 | public DisInterceptNestedScrollView(Context context) {
14 | super(context);
15 | requestDisallowInterceptTouchEvent(true);
16 | }
17 |
18 | public DisInterceptNestedScrollView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | requestDisallowInterceptTouchEvent(true);
21 | }
22 |
23 | public DisInterceptNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
24 | super(context, attrs, defStyleAttr);
25 | requestDisallowInterceptTouchEvent(true);
26 | }
27 |
28 |
29 | public boolean dispatchTouchEvent(MotionEvent ev) {
30 | getParent().requestDisallowInterceptTouchEvent(true);
31 | return super.dispatchTouchEvent(ev);
32 | }
33 |
34 | @Override
35 | public boolean onTouchEvent(MotionEvent event) {
36 | switch (event.getAction()) {
37 | case MotionEvent.ACTION_MOVE:
38 | requestDisallowInterceptTouchEvent(true);
39 | break;
40 | case MotionEvent.ACTION_UP:
41 | case MotionEvent.ACTION_CANCEL:
42 | requestDisallowInterceptTouchEvent(false);
43 | break;
44 | }
45 | return super.onTouchEvent(event);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_avater.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable-xxhdpi/ic_avater.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable-xxhdpi/ic_bg.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/icon_msg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable-xxhdpi/icon_msg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/icon_msg_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable-xxhdpi/icon_msg_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/icon_setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable-xxhdpi/icon_setting.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/icon_setting_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable-xxhdpi/icon_setting_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_ali_middle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable/ic_ali_middle.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_ali_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/drawable/ic_ali_top.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_stroke_roundcir.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_stroke_roundredcir.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_demo1.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
27 |
28 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_item_list1.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_item_list2.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_func_in_uc.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
30 |
31 |
41 |
42 |
52 |
53 |
63 |
64 |
65 |
66 |
72 |
73 |
83 |
84 |
94 |
95 |
105 |
106 |
114 |
115 |
116 |
117 |
121 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_uc_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
14 |
15 |
20 |
21 |
22 |
41 |
42 |
46 |
47 |
51 |
52 |
60 |
61 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_uc_head_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
18 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_uc_head_middle.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
24 |
25 |
33 |
34 |
41 |
42 |
50 |
51 |
62 |
63 |
64 |
65 |
66 |
67 |
71 |
72 |
83 |
84 |
85 |
86 |
87 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_uc_head_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
22 |
23 |
34 |
35 |
36 |
46 |
47 |
56 |
57 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/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 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #f5f5f5
8 | #333333
9 | #fc4f74
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 48dp
4 | 300dp
5 | 16dp
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EasyBehavior
3 |
4 | me.stefan.easybehavior.demo1.behavior.AppBarLayoutOverScrollViewBehavior
5 | me.stefan.easybehavior.demo1.behavior.CircleImageInUsercBehavior
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | apply from: "config.gradle"
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | maven {
7 | url 'https://maven.google.com/'
8 | name 'Google'
9 | }
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.2.0'
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | jcenter()
22 | maven {
23 | url 'https://maven.google.com/'
24 | name 'Google'
25 | }
26 | }
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/config.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 |
3 | configuration = [
4 | applicationId: 'me.stefan.easybehavior'
5 | ]
6 |
7 |
8 | libraries = [
9 | "support-v4" : "com.android.support:support-v4:$ANDROID_DEPENDENCE_SDK_VERSION",
10 | "appcompat-v7" : "com.android.support:appcompat-v7:$ANDROID_DEPENDENCE_SDK_VERSION",
11 | "design" : "com.android.support:design:$ANDROID_DEPENDENCE_SDK_VERSION",
12 | "palette" : "com.android.support:palette-v7:$ANDROID_DEPENDENCE_SDK_VERSION",
13 | "recyclerview-v7": "com.android.support:recyclerview-v7:$ANDROID_DEPENDENCE_SDK_VERSION",
14 | "glide" : 'com.github.bumptech.glide:glide:3.7.0',//图片加载
15 | "statusbar_util" : 'com.jaeger.statusbarutil:library:1.4.0',
16 | ]
17 |
18 | sdkKey = [
19 | ]
20 |
21 | testingLibraries = [
22 | "junit": 'junit:junit:4.12'
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/gif/Coali.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/gif/Coali.gif
--------------------------------------------------------------------------------
/gif/EasyBehavior.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/gif/EasyBehavior.gif
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | ANDROID_BUILD_MIN_SDK_VERSION=16
10 | android.enableBuildCache=true
11 | android.useDeprecatedNdk=true
12 | VERSION_NAME=2.0.2
13 | org.gradle.daemon=true
14 | VERSION_CODE=5
15 | ANDROID_BUILD_TARGET_SDK_VERSION=28
16 | org.gradle.parallel=true
17 | ANDROID_BUILD_TOOLS_VERSION=28.0.2
18 | ANDROID_BUILD_SDK_VERSION=28
19 | ANDROID_DEPENDENCE_SDK_VERSION=28.0.0
20 | org.gradle.jvmargs=-Xmx2048m
21 | org.gradle.configureondemand=true
22 | # When configured, Gradle will run in incubating parallel mode.
23 | # This option should only be used with decoupled projects. More details, visit
24 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
25 | # org.gradle.parallel=true
26 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JmStefanAndroid/EasyBehavior/2f5f14754f425405a6387acda48462e40fd6b5b9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Feb 28 14:44:22 CST 2020
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-4.6-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',':tabLayoutLibrary'
2 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | def libs = rootProject.ext.libraries
3 | android {
4 | compileSdkVersion ANDROID_BUILD_SDK_VERSION as int
5 | buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
6 | defaultConfig {
7 | minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION as int
8 | targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int
9 | }
10 | }
11 |
12 | dependencies {
13 | implementation fileTree(dir: 'libs', include: ['*.jar'])
14 | implementation libs["support-v4"]
15 | }
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/java/com/flyco/tablayout/SegmentTabLayout.java:
--------------------------------------------------------------------------------
1 | package com.flyco.tablayout;
2 |
3 | import android.animation.TypeEvaluator;
4 | import android.animation.ValueAnimator;
5 | import android.content.Context;
6 | import android.content.res.TypedArray;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.Rect;
11 | import android.graphics.drawable.GradientDrawable;
12 | import android.os.Bundle;
13 | import android.os.Parcelable;
14 | import android.support.v4.app.Fragment;
15 | import android.support.v4.app.FragmentActivity;
16 | import android.util.AttributeSet;
17 | import android.util.SparseArray;
18 | import android.util.TypedValue;
19 | import android.view.View;
20 | import android.view.ViewGroup;
21 | import android.view.animation.OvershootInterpolator;
22 | import android.widget.FrameLayout;
23 | import android.widget.LinearLayout;
24 | import android.widget.TextView;
25 |
26 | import com.flyco.tablayout.listener.OnTabSelectListener;
27 | import com.flyco.tablayout.utils.FragmentChangeManager;
28 | import com.flyco.tablayout.utils.UnreadMsgUtils;
29 | import com.flyco.tablayout.widget.MsgView;
30 |
31 | import java.util.ArrayList;
32 |
33 | public class SegmentTabLayout extends FrameLayout implements ValueAnimator.AnimatorUpdateListener {
34 | private Context mContext;
35 | private String[] mTitles;
36 | private LinearLayout mTabsContainer;
37 | private int mCurrentTab;
38 | private int mLastTab;
39 | private int mTabCount;
40 | /** 用于绘制显示器 */
41 | private Rect mIndicatorRect = new Rect();
42 | private GradientDrawable mIndicatorDrawable = new GradientDrawable();
43 | private GradientDrawable mRectDrawable = new GradientDrawable();
44 |
45 | private Paint mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
46 |
47 | private float mTabPadding;
48 | private boolean mTabSpaceEqual;
49 | private float mTabWidth;
50 |
51 | /** indicator */
52 | private int mIndicatorColor;
53 | private float mIndicatorHeight;
54 | private float mIndicatorCornerRadius;
55 | private float mIndicatorMarginLeft;
56 | private float mIndicatorMarginTop;
57 | private float mIndicatorMarginRight;
58 | private float mIndicatorMarginBottom;
59 | private long mIndicatorAnimDuration;
60 | private boolean mIndicatorAnimEnable;
61 | private boolean mIndicatorBounceEnable;
62 |
63 | /** divider */
64 | private int mDividerColor;
65 | private float mDividerWidth;
66 | private float mDividerPadding;
67 |
68 | /** title */
69 | private float mTextsize;
70 | private int mTextSelectColor;
71 | private int mTextUnselectColor;
72 | private boolean mTextBold;
73 | private boolean mTextAllCaps;
74 |
75 | private int mBarColor;
76 | private int mBarStrokeColor;
77 | private float mBarStrokeWidth;
78 |
79 | private int mHeight;
80 |
81 | /** anim */
82 | private ValueAnimator mValueAnimator;
83 | private OvershootInterpolator mInterpolator = new OvershootInterpolator(0.8f);
84 |
85 | private FragmentChangeManager mFragmentChangeManager;
86 | private float[] mRadiusArr = new float[8];
87 |
88 | public SegmentTabLayout(Context context) {
89 | this(context, null, 0);
90 | }
91 |
92 | public SegmentTabLayout(Context context, AttributeSet attrs) {
93 | this(context, attrs, 0);
94 | }
95 |
96 | public SegmentTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
97 | super(context, attrs, defStyleAttr);
98 | setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag
99 | setClipChildren(false);
100 | setClipToPadding(false);
101 |
102 | this.mContext = context;
103 | mTabsContainer = new LinearLayout(context);
104 | addView(mTabsContainer);
105 |
106 | obtainAttributes(context, attrs);
107 |
108 | //get layout_height
109 | String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height");
110 |
111 | //create ViewPager
112 | if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) {
113 | } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) {
114 | } else {
115 | int[] systemAttrs = {android.R.attr.layout_height};
116 | TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs);
117 | mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT);
118 | a.recycle();
119 | }
120 |
121 | mValueAnimator = ValueAnimator.ofObject(new PointEvaluator(), mLastP, mCurrentP);
122 | mValueAnimator.addUpdateListener(this);
123 | }
124 |
125 | private void obtainAttributes(Context context, AttributeSet attrs) {
126 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SegmentTabLayout);
127 |
128 | mIndicatorColor = ta.getColor(R.styleable.SegmentTabLayout_tl_indicator_color, Color.parseColor("#222831"));
129 | mIndicatorHeight = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_height, -1);
130 | mIndicatorCornerRadius = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_corner_radius, -1);
131 | mIndicatorMarginLeft = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_left, dp2px(0));
132 | mIndicatorMarginTop = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_top, 0);
133 | mIndicatorMarginRight = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_right, dp2px(0));
134 | mIndicatorMarginBottom = ta.getDimension(R.styleable.SegmentTabLayout_tl_indicator_margin_bottom, 0);
135 | mIndicatorAnimEnable = ta.getBoolean(R.styleable.SegmentTabLayout_tl_indicator_anim_enable, false);
136 | mIndicatorBounceEnable = ta.getBoolean(R.styleable.SegmentTabLayout_tl_indicator_bounce_enable, true);
137 | mIndicatorAnimDuration = ta.getInt(R.styleable.SegmentTabLayout_tl_indicator_anim_duration, -1);
138 |
139 | mDividerColor = ta.getColor(R.styleable.SegmentTabLayout_tl_divider_color, mIndicatorColor);
140 | mDividerWidth = ta.getDimension(R.styleable.SegmentTabLayout_tl_divider_width, dp2px(1));
141 | mDividerPadding = ta.getDimension(R.styleable.SegmentTabLayout_tl_divider_padding, 0);
142 |
143 | mTextsize = ta.getDimension(R.styleable.SegmentTabLayout_tl_textsize, sp2px(13f));
144 | mTextSelectColor = ta.getColor(R.styleable.SegmentTabLayout_tl_textSelectColor, Color.parseColor("#ffffff"));
145 | mTextUnselectColor = ta.getColor(R.styleable.SegmentTabLayout_tl_textUnselectColor, mIndicatorColor);
146 | mTextBold = ta.getBoolean(R.styleable.SegmentTabLayout_tl_textBold, false);
147 | mTextAllCaps = ta.getBoolean(R.styleable.SegmentTabLayout_tl_textAllCaps, false);
148 |
149 | mTabSpaceEqual = ta.getBoolean(R.styleable.SegmentTabLayout_tl_tab_space_equal, true);
150 | mTabWidth = ta.getDimension(R.styleable.SegmentTabLayout_tl_tab_width, dp2px(-1));
151 | mTabPadding = ta.getDimension(R.styleable.SegmentTabLayout_tl_tab_padding, mTabSpaceEqual || mTabWidth > 0 ? dp2px(0) : dp2px(10));
152 |
153 | mBarColor = ta.getColor(R.styleable.SegmentTabLayout_tl_bar_color, Color.TRANSPARENT);
154 | mBarStrokeColor = ta.getColor(R.styleable.SegmentTabLayout_tl_bar_stroke_color, mIndicatorColor);
155 | mBarStrokeWidth = ta.getDimension(R.styleable.SegmentTabLayout_tl_bar_stroke_width, dp2px(1));
156 |
157 | ta.recycle();
158 | }
159 |
160 | public void setTabData(String[] titles) {
161 | if (titles == null || titles.length == 0) {
162 | throw new IllegalStateException("Titles can not be NULL or EMPTY !");
163 | }
164 |
165 | this.mTitles = titles;
166 |
167 | notifyDataSetChanged();
168 | }
169 |
170 | /** 关联数据支持同时切换fragments */
171 | public void setTabData(String[] titles, FragmentActivity fa, int containerViewId, ArrayList fragments) {
172 | mFragmentChangeManager = new FragmentChangeManager(fa.getSupportFragmentManager(), containerViewId, fragments);
173 | setTabData(titles);
174 | }
175 |
176 | /** 更新数据 */
177 | public void notifyDataSetChanged() {
178 | mTabsContainer.removeAllViews();
179 | this.mTabCount = mTitles.length;
180 | View tabView;
181 | for (int i = 0; i < mTabCount; i++) {
182 | tabView = View.inflate(mContext, R.layout.layout_tab_segment, null);
183 | tabView.setTag(i);
184 | addTab(i, tabView);
185 | }
186 |
187 | updateTabStyles();
188 | }
189 |
190 | /** 创建并添加tab */
191 | private void addTab(final int position, View tabView) {
192 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
193 | tv_tab_title.setText(mTitles[position]);
194 |
195 | tabView.setOnClickListener(new OnClickListener() {
196 | @Override
197 | public void onClick(View v) {
198 | int position = (Integer) v.getTag();
199 | if (mCurrentTab != position) {
200 | setCurrentTab(position);
201 | if (mListener != null) {
202 | mListener.onTabSelect(position);
203 | }
204 | } else {
205 | if (mListener != null) {
206 | mListener.onTabReselect(position);
207 | }
208 | }
209 | }
210 | });
211 |
212 | /** 每一个Tab的布局参数 */
213 | LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ?
214 | new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) :
215 | new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
216 | if (mTabWidth > 0) {
217 | lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT);
218 | }
219 | mTabsContainer.addView(tabView, position, lp_tab);
220 | }
221 |
222 | private void updateTabStyles() {
223 | for (int i = 0; i < mTabCount; i++) {
224 | View tabView = mTabsContainer.getChildAt(i);
225 | tabView.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0);
226 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
227 | tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnselectColor);
228 | tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextsize);
229 | // tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0);
230 | if (mTextAllCaps) {
231 | tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase());
232 | }
233 |
234 | if (mTextBold) {
235 | tv_tab_title.getPaint().setFakeBoldText(mTextBold);
236 | }
237 | }
238 | }
239 |
240 | private void updateTabSelection(int position) {
241 | for (int i = 0; i < mTabCount; ++i) {
242 | View tabView = mTabsContainer.getChildAt(i);
243 | final boolean isSelect = i == position;
244 | TextView tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
245 | tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnselectColor);
246 | }
247 | }
248 |
249 | private void calcOffset() {
250 | final View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab);
251 | mCurrentP.left = currentTabView.getLeft();
252 | mCurrentP.right = currentTabView.getRight();
253 |
254 | final View lastTabView = mTabsContainer.getChildAt(this.mLastTab);
255 | mLastP.left = lastTabView.getLeft();
256 | mLastP.right = lastTabView.getRight();
257 |
258 | // Log.d("AAA", "mLastP--->" + mLastP.left + "&" + mLastP.right);
259 | // Log.d("AAA", "mCurrentP--->" + mCurrentP.left + "&" + mCurrentP.right);
260 | if (mLastP.left == mCurrentP.left && mLastP.right == mCurrentP.right) {
261 | invalidate();
262 | } else {
263 | mValueAnimator.setObjectValues(mLastP, mCurrentP);
264 | if (mIndicatorBounceEnable) {
265 | mValueAnimator.setInterpolator(mInterpolator);
266 | }
267 |
268 | if (mIndicatorAnimDuration < 0) {
269 | mIndicatorAnimDuration = mIndicatorBounceEnable ? 500 : 250;
270 | }
271 | mValueAnimator.setDuration(mIndicatorAnimDuration);
272 | mValueAnimator.start();
273 | }
274 | }
275 |
276 | private void calcIndicatorRect() {
277 | View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab);
278 | float left = currentTabView.getLeft();
279 | float right = currentTabView.getRight();
280 |
281 | mIndicatorRect.left = (int) left;
282 | mIndicatorRect.right = (int) right;
283 |
284 | if (!mIndicatorAnimEnable) {
285 | if (mCurrentTab == 0) {
286 | /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/
287 | mRadiusArr[0] = mIndicatorCornerRadius;
288 | mRadiusArr[1] = mIndicatorCornerRadius;
289 | mRadiusArr[2] = 0;
290 | mRadiusArr[3] = 0;
291 | mRadiusArr[4] = 0;
292 | mRadiusArr[5] = 0;
293 | mRadiusArr[6] = mIndicatorCornerRadius;
294 | mRadiusArr[7] = mIndicatorCornerRadius;
295 | } else if (mCurrentTab == mTabCount - 1) {
296 | /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/
297 | mRadiusArr[0] = 0;
298 | mRadiusArr[1] = 0;
299 | mRadiusArr[2] = mIndicatorCornerRadius;
300 | mRadiusArr[3] = mIndicatorCornerRadius;
301 | mRadiusArr[4] = mIndicatorCornerRadius;
302 | mRadiusArr[5] = mIndicatorCornerRadius;
303 | mRadiusArr[6] = 0;
304 | mRadiusArr[7] = 0;
305 | } else {
306 | /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/
307 | mRadiusArr[0] = 0;
308 | mRadiusArr[1] = 0;
309 | mRadiusArr[2] = 0;
310 | mRadiusArr[3] = 0;
311 | mRadiusArr[4] = 0;
312 | mRadiusArr[5] = 0;
313 | mRadiusArr[6] = 0;
314 | mRadiusArr[7] = 0;
315 | }
316 | } else {
317 | /**The corners are ordered top-left, top-right, bottom-right, bottom-left*/
318 | mRadiusArr[0] = mIndicatorCornerRadius;
319 | mRadiusArr[1] = mIndicatorCornerRadius;
320 | mRadiusArr[2] = mIndicatorCornerRadius;
321 | mRadiusArr[3] = mIndicatorCornerRadius;
322 | mRadiusArr[4] = mIndicatorCornerRadius;
323 | mRadiusArr[5] = mIndicatorCornerRadius;
324 | mRadiusArr[6] = mIndicatorCornerRadius;
325 | mRadiusArr[7] = mIndicatorCornerRadius;
326 | }
327 | }
328 |
329 | @Override
330 | public void onAnimationUpdate(ValueAnimator animation) {
331 | IndicatorPoint p = (IndicatorPoint) animation.getAnimatedValue();
332 | mIndicatorRect.left = (int) p.left;
333 | mIndicatorRect.right = (int) p.right;
334 | invalidate();
335 | }
336 |
337 | private boolean mIsFirstDraw = true;
338 |
339 | @Override
340 | protected void onDraw(Canvas canvas) {
341 | super.onDraw(canvas);
342 |
343 | if (isInEditMode() || mTabCount <= 0) {
344 | return;
345 | }
346 |
347 | int height = getHeight();
348 | int paddingLeft = getPaddingLeft();
349 |
350 | if (mIndicatorHeight < 0) {
351 | mIndicatorHeight = height - mIndicatorMarginTop - mIndicatorMarginBottom;
352 | }
353 |
354 | if (mIndicatorCornerRadius < 0 || mIndicatorCornerRadius > mIndicatorHeight / 2) {
355 | mIndicatorCornerRadius = mIndicatorHeight / 2;
356 | }
357 |
358 | //draw rect
359 | mRectDrawable.setColor(mBarColor);
360 | mRectDrawable.setStroke((int) mBarStrokeWidth, mBarStrokeColor);
361 | mRectDrawable.setCornerRadius(mIndicatorCornerRadius);
362 | mRectDrawable.setBounds(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
363 | mRectDrawable.draw(canvas);
364 |
365 | // draw divider
366 | if (!mIndicatorAnimEnable && mDividerWidth > 0) {
367 | mDividerPaint.setStrokeWidth(mDividerWidth);
368 | mDividerPaint.setColor(mDividerColor);
369 | for (int i = 0; i < mTabCount - 1; i++) {
370 | View tab = mTabsContainer.getChildAt(i);
371 | canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint);
372 | }
373 | }
374 |
375 |
376 | //draw indicator line
377 | if (mIndicatorAnimEnable) {
378 | if (mIsFirstDraw) {
379 | mIsFirstDraw = false;
380 | calcIndicatorRect();
381 | }
382 | } else {
383 | calcIndicatorRect();
384 | }
385 |
386 | mIndicatorDrawable.setColor(mIndicatorColor);
387 | mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left,
388 | (int) mIndicatorMarginTop, (int) (paddingLeft + mIndicatorRect.right - mIndicatorMarginRight),
389 | (int) (mIndicatorMarginTop + mIndicatorHeight));
390 | mIndicatorDrawable.setCornerRadii(mRadiusArr);
391 | mIndicatorDrawable.draw(canvas);
392 |
393 | }
394 |
395 | //setter and getter
396 | public void setCurrentTab(int currentTab) {
397 | mLastTab = this.mCurrentTab;
398 | this.mCurrentTab = currentTab;
399 | updateTabSelection(currentTab);
400 | if (mFragmentChangeManager != null) {
401 | mFragmentChangeManager.setFragments(currentTab);
402 | }
403 | if (mIndicatorAnimEnable) {
404 | calcOffset();
405 | } else {
406 | invalidate();
407 | }
408 | }
409 |
410 | public void setTabPadding(float tabPadding) {
411 | this.mTabPadding = dp2px(tabPadding);
412 | updateTabStyles();
413 | }
414 |
415 | public void setTabSpaceEqual(boolean tabSpaceEqual) {
416 | this.mTabSpaceEqual = tabSpaceEqual;
417 | updateTabStyles();
418 | }
419 |
420 | public void setTabWidth(float tabWidth) {
421 | this.mTabWidth = dp2px(tabWidth);
422 | updateTabStyles();
423 | }
424 |
425 | public void setIndicatorColor(int indicatorColor) {
426 | this.mIndicatorColor = indicatorColor;
427 | invalidate();
428 | }
429 |
430 | public void setIndicatorHeight(float indicatorHeight) {
431 | this.mIndicatorHeight = dp2px(indicatorHeight);
432 | invalidate();
433 | }
434 |
435 | public void setIndicatorCornerRadius(float indicatorCornerRadius) {
436 | this.mIndicatorCornerRadius = dp2px(indicatorCornerRadius);
437 | invalidate();
438 | }
439 |
440 | public void setIndicatorMargin(float indicatorMarginLeft, float indicatorMarginTop,
441 | float indicatorMarginRight, float indicatorMarginBottom) {
442 | this.mIndicatorMarginLeft = dp2px(indicatorMarginLeft);
443 | this.mIndicatorMarginTop = dp2px(indicatorMarginTop);
444 | this.mIndicatorMarginRight = dp2px(indicatorMarginRight);
445 | this.mIndicatorMarginBottom = dp2px(indicatorMarginBottom);
446 | invalidate();
447 | }
448 |
449 | public void setIndicatorAnimDuration(long indicatorAnimDuration) {
450 | this.mIndicatorAnimDuration = indicatorAnimDuration;
451 | }
452 |
453 | public void setIndicatorAnimEnable(boolean indicatorAnimEnable) {
454 | this.mIndicatorAnimEnable = indicatorAnimEnable;
455 | }
456 |
457 | public void setIndicatorBounceEnable(boolean indicatorBounceEnable) {
458 | this.mIndicatorBounceEnable = indicatorBounceEnable;
459 | }
460 |
461 | public void setDividerColor(int dividerColor) {
462 | this.mDividerColor = dividerColor;
463 | invalidate();
464 | }
465 |
466 | public void setDividerWidth(float dividerWidth) {
467 | this.mDividerWidth = dp2px(dividerWidth);
468 | invalidate();
469 | }
470 |
471 | public void setDividerPadding(float dividerPadding) {
472 | this.mDividerPadding = dp2px(dividerPadding);
473 | invalidate();
474 | }
475 |
476 | public void setTextsize(float textsize) {
477 | this.mTextsize = sp2px(textsize);
478 | updateTabStyles();
479 | }
480 |
481 | public void setTextSelectColor(int textSelectColor) {
482 | this.mTextSelectColor = textSelectColor;
483 | updateTabStyles();
484 | }
485 |
486 | public void setTextUnselectColor(int textUnselectColor) {
487 | this.mTextUnselectColor = textUnselectColor;
488 | updateTabStyles();
489 | }
490 |
491 | public void setTextBold(boolean textBold) {
492 | this.mTextBold = textBold;
493 | updateTabStyles();
494 | }
495 |
496 | public void setTextAllCaps(boolean textAllCaps) {
497 | this.mTextAllCaps = textAllCaps;
498 | updateTabStyles();
499 | }
500 |
501 | public int getTabCount() {
502 | return mTabCount;
503 | }
504 |
505 | public int getCurrentTab() {
506 | return mCurrentTab;
507 | }
508 |
509 | public float getTabPadding() {
510 | return mTabPadding;
511 | }
512 |
513 | public boolean isTabSpaceEqual() {
514 | return mTabSpaceEqual;
515 | }
516 |
517 | public float getTabWidth() {
518 | return mTabWidth;
519 | }
520 |
521 | public int getIndicatorColor() {
522 | return mIndicatorColor;
523 | }
524 |
525 | public float getIndicatorHeight() {
526 | return mIndicatorHeight;
527 | }
528 |
529 | public float getIndicatorCornerRadius() {
530 | return mIndicatorCornerRadius;
531 | }
532 |
533 | public float getIndicatorMarginLeft() {
534 | return mIndicatorMarginLeft;
535 | }
536 |
537 | public float getIndicatorMarginTop() {
538 | return mIndicatorMarginTop;
539 | }
540 |
541 | public float getIndicatorMarginRight() {
542 | return mIndicatorMarginRight;
543 | }
544 |
545 | public float getIndicatorMarginBottom() {
546 | return mIndicatorMarginBottom;
547 | }
548 |
549 | public long getIndicatorAnimDuration() {
550 | return mIndicatorAnimDuration;
551 | }
552 |
553 | public boolean isIndicatorAnimEnable() {
554 | return mIndicatorAnimEnable;
555 | }
556 |
557 | public boolean isIndicatorBounceEnable() {
558 | return mIndicatorBounceEnable;
559 | }
560 |
561 | public int getDividerColor() {
562 | return mDividerColor;
563 | }
564 |
565 | public float getDividerWidth() {
566 | return mDividerWidth;
567 | }
568 |
569 | public float getDividerPadding() {
570 | return mDividerPadding;
571 | }
572 |
573 | public float getTextsize() {
574 | return mTextsize;
575 | }
576 |
577 | public int getTextSelectColor() {
578 | return mTextSelectColor;
579 | }
580 |
581 | public int getTextUnselectColor() {
582 | return mTextUnselectColor;
583 | }
584 |
585 | public boolean isTextBold() {
586 | return mTextBold;
587 | }
588 |
589 | public boolean isTextAllCaps() {
590 | return mTextAllCaps;
591 | }
592 |
593 | public TextView getTitleView(int tab) {
594 | View tabView = mTabsContainer.getChildAt(tab);
595 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
596 | return tv_tab_title;
597 | }
598 |
599 | //setter and getter
600 | // show MsgTipView
601 | private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
602 | private SparseArray mInitSetMap = new SparseArray<>();
603 |
604 | /**
605 | * 显示未读消息
606 | *
607 | * @param position 显示tab位置
608 | * @param num num小于等于0显示红点,num大于0显示数字
609 | */
610 | public void showMsg(int position, int num) {
611 | if (position >= mTabCount) {
612 | position = mTabCount - 1;
613 | }
614 |
615 | View tabView = mTabsContainer.getChildAt(position);
616 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
617 | if (tipView != null) {
618 | UnreadMsgUtils.show(tipView, num);
619 |
620 | if (mInitSetMap.get(position) != null && mInitSetMap.get(position)) {
621 | return;
622 | }
623 |
624 | setMsgMargin(position, 2, 2);
625 |
626 | mInitSetMap.put(position, true);
627 | }
628 | }
629 |
630 | /**
631 | * 显示未读红点
632 | *
633 | * @param position 显示tab位置
634 | */
635 | public void showDot(int position) {
636 | if (position >= mTabCount) {
637 | position = mTabCount - 1;
638 | }
639 | showMsg(position, 0);
640 | }
641 |
642 | public void hideMsg(int position) {
643 | if (position >= mTabCount) {
644 | position = mTabCount - 1;
645 | }
646 |
647 | View tabView = mTabsContainer.getChildAt(position);
648 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
649 | if (tipView != null) {
650 | tipView.setVisibility(View.GONE);
651 | }
652 | }
653 |
654 | /**
655 | * 设置提示红点偏移,注意
656 | * 1.控件为固定高度:参照点为tab内容的右上角
657 | * 2.控件高度不固定(WRAP_CONTENT):参照点为tab内容的右上角,此时高度已是红点的最高显示范围,所以这时bottomPadding其实就是topPadding
658 | */
659 | public void setMsgMargin(int position, float leftPadding, float bottomPadding) {
660 | if (position >= mTabCount) {
661 | position = mTabCount - 1;
662 | }
663 | View tabView = mTabsContainer.getChildAt(position);
664 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
665 | if (tipView != null) {
666 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
667 | mTextPaint.setTextSize(mTextsize);
668 | float textWidth = mTextPaint.measureText(tv_tab_title.getText().toString());
669 | float textHeight = mTextPaint.descent() - mTextPaint.ascent();
670 | MarginLayoutParams lp = (MarginLayoutParams) tipView.getLayoutParams();
671 |
672 | lp.leftMargin = dp2px(leftPadding);
673 | lp.topMargin = mHeight > 0 ? (int) (mHeight - textHeight) / 2 - dp2px(bottomPadding) : dp2px(bottomPadding);
674 |
675 | tipView.setLayoutParams(lp);
676 | }
677 | }
678 |
679 | /** 当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置 */
680 | public MsgView getMsgView(int position) {
681 | if (position >= mTabCount) {
682 | position = mTabCount - 1;
683 | }
684 | View tabView = mTabsContainer.getChildAt(position);
685 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
686 | return tipView;
687 | }
688 |
689 | private OnTabSelectListener mListener;
690 |
691 | public void setOnTabSelectListener(OnTabSelectListener listener) {
692 | this.mListener = listener;
693 | }
694 |
695 | @Override
696 | protected Parcelable onSaveInstanceState() {
697 | Bundle bundle = new Bundle();
698 | bundle.putParcelable("instanceState", super.onSaveInstanceState());
699 | bundle.putInt("mCurrentTab", mCurrentTab);
700 | return bundle;
701 | }
702 |
703 | @Override
704 | protected void onRestoreInstanceState(Parcelable state) {
705 | if (state instanceof Bundle) {
706 | Bundle bundle = (Bundle) state;
707 | mCurrentTab = bundle.getInt("mCurrentTab");
708 | state = bundle.getParcelable("instanceState");
709 | if (mCurrentTab != 0 && mTabsContainer.getChildCount() > 0) {
710 | updateTabSelection(mCurrentTab);
711 | }
712 | }
713 | super.onRestoreInstanceState(state);
714 | }
715 |
716 | class IndicatorPoint {
717 | public float left;
718 | public float right;
719 | }
720 |
721 | private IndicatorPoint mCurrentP = new IndicatorPoint();
722 | private IndicatorPoint mLastP = new IndicatorPoint();
723 |
724 | class PointEvaluator implements TypeEvaluator {
725 | @Override
726 | public IndicatorPoint evaluate(float fraction, IndicatorPoint startValue, IndicatorPoint endValue) {
727 | float left = startValue.left + fraction * (endValue.left - startValue.left);
728 | float right = startValue.right + fraction * (endValue.right - startValue.right);
729 | IndicatorPoint point = new IndicatorPoint();
730 | point.left = left;
731 | point.right = right;
732 | return point;
733 | }
734 | }
735 |
736 | protected int dp2px(float dp) {
737 | final float scale = mContext.getResources().getDisplayMetrics().density;
738 | return (int) (dp * scale + 0.5f);
739 | }
740 |
741 | protected int sp2px(float sp) {
742 | final float scale = this.mContext.getResources().getDisplayMetrics().scaledDensity;
743 | return (int) (sp * scale + 0.5f);
744 | }
745 | }
746 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/java/com/flyco/tablayout/SlidingTabLayout.java:
--------------------------------------------------------------------------------
1 | package com.flyco.tablayout;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.Rect;
10 | import android.graphics.drawable.GradientDrawable;
11 | import android.os.Bundle;
12 | import android.os.Parcelable;
13 | import android.support.v4.app.Fragment;
14 | import android.support.v4.app.FragmentActivity;
15 | import android.support.v4.app.FragmentManager;
16 | import android.support.v4.app.FragmentPagerAdapter;
17 | import android.support.v4.view.PagerAdapter;
18 | import android.support.v4.view.ViewPager;
19 | import android.util.AttributeSet;
20 | import android.util.SparseArray;
21 | import android.util.TypedValue;
22 | import android.view.Gravity;
23 | import android.view.View;
24 | import android.view.ViewGroup;
25 | import android.widget.HorizontalScrollView;
26 | import android.widget.LinearLayout;
27 | import android.widget.TextView;
28 |
29 | import com.flyco.tablayout.listener.OnTabSelectListener;
30 | import com.flyco.tablayout.utils.UnreadMsgUtils;
31 | import com.flyco.tablayout.widget.MsgView;
32 |
33 | import java.util.ArrayList;
34 | import java.util.Collections;
35 |
36 | /** 滑动TabLayout,对于ViewPager的依赖性强 */
37 | public class SlidingTabLayout extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
38 | private Context mContext;
39 | private ViewPager mViewPager;
40 | private ArrayList mTitles;
41 | private LinearLayout mTabsContainer;
42 | private int mCurrentTab;
43 | private float mCurrentPositionOffset;
44 | private int mTabCount;
45 | /** 用于绘制显示器 */
46 | private Rect mIndicatorRect = new Rect();
47 | /** 用于实现滚动居中 */
48 | private Rect mTabRect = new Rect();
49 | private GradientDrawable mIndicatorDrawable = new GradientDrawable();
50 |
51 | private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
52 | private Paint mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
53 | private Paint mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
54 | private Path mTrianglePath = new Path();
55 | private static final int STYLE_NORMAL = 0;
56 | private static final int STYLE_TRIANGLE = 1;
57 | private static final int STYLE_BLOCK = 2;
58 | private int mIndicatorStyle = STYLE_NORMAL;
59 |
60 | private float mTabPadding;
61 | private boolean mTabSpaceEqual;
62 | private float mTabWidth;
63 |
64 | /** indicator */
65 | private int mIndicatorColor;
66 | private float mIndicatorHeight;
67 | private float mIndicatorWidth;
68 | private float mIndicatorCornerRadius;
69 | private float mIndicatorMarginLeft;
70 | private float mIndicatorMarginTop;
71 | private float mIndicatorMarginRight;
72 | private float mIndicatorMarginBottom;
73 | private int mIndicatorGravity;
74 | private boolean mIndicatorWidthEqualTitle;
75 |
76 | /** underline */
77 | private int mUnderlineColor;
78 | private float mUnderlineHeight;
79 | private int mUnderlineGravity;
80 |
81 | /** divider */
82 | private int mDividerColor;
83 | private float mDividerWidth;
84 | private float mDividerPadding;
85 |
86 | /** title */
87 | private float mTextsize;
88 | private int mTextSelectColor;
89 | private int mTextUnselectColor;
90 | private boolean mTextBold;
91 | private boolean mTextAllCaps;
92 |
93 | private int mLastScrollX;
94 | private int mHeight;
95 |
96 | public SlidingTabLayout(Context context) {
97 | this(context, null, 0);
98 | }
99 |
100 | public SlidingTabLayout(Context context, AttributeSet attrs) {
101 | this(context, attrs, 0);
102 | }
103 |
104 | public SlidingTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
105 | super(context, attrs, defStyleAttr);
106 | setFillViewport(true);//设置滚动视图是否可以伸缩其内容以填充视口
107 | setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag
108 | setClipChildren(false);
109 | setClipToPadding(false);
110 |
111 | this.mContext = context;
112 | mTabsContainer = new LinearLayout(context);
113 | addView(mTabsContainer);
114 |
115 | obtainAttributes(context, attrs);
116 |
117 | //get layout_height
118 | String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height");
119 |
120 | //create ViewPager
121 | if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) {
122 | } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) {
123 | } else {
124 | int[] systemAttrs = {android.R.attr.layout_height};
125 | TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs);
126 | mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT);
127 | a.recycle();
128 | }
129 | }
130 |
131 | private void obtainAttributes(Context context, AttributeSet attrs) {
132 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingTabLayout);
133 |
134 | mIndicatorStyle = ta.getInt(R.styleable.SlidingTabLayout_tl_indicator_style, STYLE_NORMAL);
135 | mIndicatorColor = ta.getColor(R.styleable.SlidingTabLayout_tl_indicator_color, Color.parseColor(mIndicatorStyle == STYLE_BLOCK ? "#4B6A87" : "#ffffff"));
136 | mIndicatorHeight = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_height,
137 | dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 4 : (mIndicatorStyle == STYLE_BLOCK ? -1 : 2)));
138 | mIndicatorWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_width, dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 10 : -1));
139 | mIndicatorCornerRadius = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_corner_radius, dp2px(mIndicatorStyle == STYLE_BLOCK ? -1 : 0));
140 | mIndicatorMarginLeft = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_left, dp2px(0));
141 | mIndicatorMarginTop = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_top, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0));
142 | mIndicatorMarginRight = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_right, dp2px(0));
143 | mIndicatorMarginBottom = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_bottom, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0));
144 | mIndicatorGravity = ta.getInt(R.styleable.SlidingTabLayout_tl_indicator_gravity, Gravity.BOTTOM);
145 | mIndicatorWidthEqualTitle = ta.getBoolean(R.styleable.SlidingTabLayout_tl_indicator_width_equal_title, false);
146 |
147 | mUnderlineColor = ta.getColor(R.styleable.SlidingTabLayout_tl_underline_color, Color.parseColor("#ffffff"));
148 | mUnderlineHeight = ta.getDimension(R.styleable.SlidingTabLayout_tl_underline_height, dp2px(0));
149 | mUnderlineGravity = ta.getInt(R.styleable.SlidingTabLayout_tl_underline_gravity, Gravity.BOTTOM);
150 |
151 | mDividerColor = ta.getColor(R.styleable.SlidingTabLayout_tl_divider_color, Color.parseColor("#ffffff"));
152 | mDividerWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_divider_width, dp2px(0));
153 | mDividerPadding = ta.getDimension(R.styleable.SlidingTabLayout_tl_divider_padding, dp2px(12));
154 |
155 | mTextsize = ta.getDimension(R.styleable.SlidingTabLayout_tl_textsize, sp2px(14));
156 | mTextSelectColor = ta.getColor(R.styleable.SlidingTabLayout_tl_textSelectColor, Color.parseColor("#ffffff"));
157 | mTextUnselectColor = ta.getColor(R.styleable.SlidingTabLayout_tl_textUnselectColor, Color.parseColor("#AAffffff"));
158 | mTextBold = ta.getBoolean(R.styleable.SlidingTabLayout_tl_textBold, false);
159 | mTextAllCaps = ta.getBoolean(R.styleable.SlidingTabLayout_tl_textAllCaps, false);
160 |
161 | mTabSpaceEqual = ta.getBoolean(R.styleable.SlidingTabLayout_tl_tab_space_equal, false);
162 | mTabWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_tab_width, dp2px(-1));
163 | mTabPadding = ta.getDimension(R.styleable.SlidingTabLayout_tl_tab_padding, mTabSpaceEqual || mTabWidth > 0 ? dp2px(0) : dp2px(20));
164 |
165 | ta.recycle();
166 | }
167 |
168 | /** 关联ViewPager */
169 | public void setViewPager(ViewPager vp) {
170 | if (vp == null || vp.getAdapter() == null) {
171 | throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !");
172 | }
173 |
174 | this.mViewPager = vp;
175 |
176 | this.mViewPager.removeOnPageChangeListener(this);
177 | this.mViewPager.addOnPageChangeListener(this);
178 | notifyDataSetChanged();
179 | }
180 |
181 | /** 关联ViewPager,用于不想在ViewPager适配器中设置titles数据的情况 */
182 | public void setViewPager(ViewPager vp, String[] titles) {
183 | if (vp == null || vp.getAdapter() == null) {
184 | throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !");
185 | }
186 |
187 | if (titles == null || titles.length == 0) {
188 | throw new IllegalStateException("Titles can not be EMPTY !");
189 | }
190 |
191 | if (titles.length != vp.getAdapter().getCount()) {
192 | throw new IllegalStateException("Titles length must be the same as the page count !");
193 | }
194 |
195 | this.mViewPager = vp;
196 | mTitles = new ArrayList<>();
197 | Collections.addAll(mTitles, titles);
198 |
199 | this.mViewPager.removeOnPageChangeListener(this);
200 | this.mViewPager.addOnPageChangeListener(this);
201 | notifyDataSetChanged();
202 | }
203 |
204 | /** 关联ViewPager,用于连适配器都不想自己实例化的情况 */
205 | public void setViewPager(ViewPager vp, String[] titles, FragmentActivity fa, ArrayList fragments) {
206 | if (vp == null) {
207 | throw new IllegalStateException("ViewPager can not be NULL !");
208 | }
209 |
210 | if (titles == null || titles.length == 0) {
211 | throw new IllegalStateException("Titles can not be EMPTY !");
212 | }
213 |
214 | this.mViewPager = vp;
215 | this.mViewPager.setAdapter(new InnerPagerAdapter(fa.getSupportFragmentManager(), fragments, titles));
216 |
217 | this.mViewPager.removeOnPageChangeListener(this);
218 | this.mViewPager.addOnPageChangeListener(this);
219 | notifyDataSetChanged();
220 | }
221 |
222 | /** 更新数据 */
223 | public void notifyDataSetChanged() {
224 | mTabsContainer.removeAllViews();
225 | this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size();
226 | View tabView;
227 | for (int i = 0; i < mTabCount; i++) {
228 | tabView = View.inflate(mContext, R.layout.layout_tab, null);
229 | CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(i) : mTitles.get(i);
230 | addTab(i, pageTitle.toString(), tabView);
231 | }
232 |
233 | updateTabStyles();
234 | }
235 |
236 | public void addNewTab(String title) {
237 | View tabView = View.inflate(mContext, R.layout.layout_tab, null);
238 | if (mTitles != null) {
239 | mTitles.add(title);
240 | }
241 |
242 | CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(mTabCount) : mTitles.get(mTabCount);
243 | addTab(mTabCount, pageTitle.toString(), tabView);
244 | this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size();
245 |
246 | updateTabStyles();
247 | }
248 |
249 | /** 创建并添加tab */
250 | private void addTab(final int position, String title, View tabView) {
251 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
252 | if (tv_tab_title != null) {
253 | if (title != null) tv_tab_title.setText(title);
254 | }
255 |
256 | tabView.setOnClickListener(new OnClickListener() {
257 | @Override
258 | public void onClick(View v) {
259 | int position = mTabsContainer.indexOfChild(v);
260 | if (position != -1) {
261 | if (mViewPager.getCurrentItem() != position) {
262 | mViewPager.setCurrentItem(position);
263 | if (mListener != null) {
264 | mListener.onTabSelect(position);
265 | }
266 | } else {
267 | if (mListener != null) {
268 | mListener.onTabReselect(position);
269 | }
270 | }
271 | }
272 | }
273 | });
274 |
275 | /** 每一个Tab的布局参数 */
276 | LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ?
277 | new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) :
278 | new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
279 | if (mTabWidth > 0) {
280 | lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT);
281 | }
282 |
283 | mTabsContainer.addView(tabView, position, lp_tab);
284 | }
285 |
286 | private void updateTabStyles() {
287 | for (int i = 0; i < mTabCount; i++) {
288 | View v = mTabsContainer.getChildAt(i);
289 | // v.setPadding((int) mTabPadding, v.getPaddingTop(), (int) mTabPadding, v.getPaddingBottom());
290 | TextView tv_tab_title = (TextView) v.findViewById(R.id.tv_tab_title);
291 | if (tv_tab_title != null) {
292 | tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnselectColor);
293 | tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextsize);
294 | tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0);
295 | if (mTextAllCaps) {
296 | tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase());
297 | }
298 |
299 | if (mTextBold) {
300 | tv_tab_title.getPaint().setFakeBoldText(mTextBold);
301 | }
302 | }
303 | }
304 | }
305 |
306 | @Override
307 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
308 | /**
309 | * position:当前View的位置
310 | * mCurrentPositionOffset:当前View的偏移量比例.[0,1)
311 | */
312 | this.mCurrentTab = position;
313 | this.mCurrentPositionOffset = positionOffset;
314 | scrollToCurrentTab();
315 | invalidate();
316 | }
317 |
318 | @Override
319 | public void onPageSelected(int position) {
320 | updateTabSelection(position);
321 | }
322 |
323 | @Override
324 | public void onPageScrollStateChanged(int state) {
325 | }
326 |
327 | /** HorizontalScrollView滚到当前tab,并且居中显示 */
328 | private void scrollToCurrentTab() {
329 | if (mTabCount <= 0) {
330 | return;
331 | }
332 |
333 | int offset = (int) (mCurrentPositionOffset * mTabsContainer.getChildAt(mCurrentTab).getWidth());
334 | /**当前Tab的left+当前Tab的Width乘以positionOffset*/
335 | int newScrollX = mTabsContainer.getChildAt(mCurrentTab).getLeft() + offset;
336 |
337 | if (mCurrentTab > 0 || offset > 0) {
338 | /**HorizontalScrollView移动到当前tab,并居中*/
339 | newScrollX -= getWidth() / 2 - getPaddingLeft();
340 | calcIndicatorRect();
341 | newScrollX += ((mTabRect.right - mTabRect.left) / 2);
342 | }
343 |
344 | if (newScrollX != mLastScrollX) {
345 | mLastScrollX = newScrollX;
346 | /** scrollTo(int x,int y):x,y代表的不是坐标点,而是偏移量
347 | * x:表示离起始位置的x水平方向的偏移量
348 | * y:表示离起始位置的y垂直方向的偏移量
349 | */
350 | scrollTo(newScrollX, 0);
351 | }
352 | }
353 |
354 | private void updateTabSelection(int position) {
355 | for (int i = 0; i < mTabCount; ++i) {
356 | View tabView = mTabsContainer.getChildAt(i);
357 | final boolean isSelect = i == position;
358 | if (tabView != null) {
359 | TextView tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
360 |
361 | if (tab_title != null) {
362 | tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnselectColor);
363 | }
364 | }
365 |
366 | }
367 | }
368 |
369 | private float margin;
370 |
371 | private void calcIndicatorRect() {
372 | View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab);
373 | float left = currentTabView.getLeft();
374 | float right = currentTabView.getRight();
375 |
376 | //for mIndicatorWidthEqualTitle
377 | if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) {
378 | TextView tab_title = (TextView) currentTabView.findViewById(R.id.tv_tab_title);
379 | mTextPaint.setTextSize(mTextsize);
380 | float textWidth = mTextPaint.measureText(tab_title.getText().toString());
381 | margin = (right - left - textWidth) / 2;
382 | }
383 |
384 | if (this.mCurrentTab < mTabCount - 1) {
385 | View nextTabView = mTabsContainer.getChildAt(this.mCurrentTab + 1);
386 | float nextTabLeft = nextTabView.getLeft();
387 | float nextTabRight = nextTabView.getRight();
388 |
389 | left = left + mCurrentPositionOffset * (nextTabLeft - left);
390 | right = right + mCurrentPositionOffset * (nextTabRight - right);
391 |
392 | //for mIndicatorWidthEqualTitle
393 | if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) {
394 | TextView next_tab_title = (TextView) nextTabView.findViewById(R.id.tv_tab_title);
395 | mTextPaint.setTextSize(mTextsize);
396 | float nextTextWidth = mTextPaint.measureText(next_tab_title.getText().toString());
397 | float nextMargin = (nextTabRight - nextTabLeft - nextTextWidth) / 2;
398 | margin = margin + mCurrentPositionOffset * (nextMargin - margin);
399 | }
400 | }
401 |
402 | mIndicatorRect.left = (int) left;
403 | mIndicatorRect.right = (int) right;
404 | //for mIndicatorWidthEqualTitle
405 | if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) {
406 | mIndicatorRect.left = (int) (left + margin - 1);
407 | mIndicatorRect.right = (int) (right - margin - 1);
408 | }
409 |
410 | mTabRect.left = (int) left;
411 | mTabRect.right = (int) right;
412 |
413 | if (mIndicatorWidth < 0) { //indicatorWidth小于0时,原jpardogo's PagerSlidingTabStrip
414 |
415 | } else {//indicatorWidth大于0时,圆角矩形以及三角形
416 | float indicatorLeft = currentTabView.getLeft() + (currentTabView.getWidth() - mIndicatorWidth) / 2;
417 |
418 | if (this.mCurrentTab < mTabCount - 1) {
419 | View nextTab = mTabsContainer.getChildAt(this.mCurrentTab + 1);
420 | indicatorLeft = indicatorLeft + mCurrentPositionOffset * (currentTabView.getWidth() / 2 + nextTab.getWidth() / 2);
421 | }
422 |
423 | mIndicatorRect.left = (int) indicatorLeft;
424 | mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth);
425 | }
426 | }
427 |
428 | @Override
429 | protected void onDraw(Canvas canvas) {
430 | super.onDraw(canvas);
431 |
432 | if (isInEditMode() || mTabCount <= 0) {
433 | return;
434 | }
435 |
436 | int height = getHeight();
437 | int paddingLeft = getPaddingLeft();
438 | // draw divider
439 | if (mDividerWidth > 0) {
440 | mDividerPaint.setStrokeWidth(mDividerWidth);
441 | mDividerPaint.setColor(mDividerColor);
442 | for (int i = 0; i < mTabCount - 1; i++) {
443 | View tab = mTabsContainer.getChildAt(i);
444 | canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint);
445 | }
446 | }
447 |
448 | // draw underline
449 | if (mUnderlineHeight > 0) {
450 | mRectPaint.setColor(mUnderlineColor);
451 | if (mUnderlineGravity == Gravity.BOTTOM) {
452 | canvas.drawRect(paddingLeft, height - mUnderlineHeight, mTabsContainer.getWidth() + paddingLeft, height, mRectPaint);
453 | } else {
454 | canvas.drawRect(paddingLeft, 0, mTabsContainer.getWidth() + paddingLeft, mUnderlineHeight, mRectPaint);
455 | }
456 | }
457 |
458 | //draw indicator line
459 |
460 | calcIndicatorRect();
461 | if (mIndicatorStyle == STYLE_TRIANGLE) {
462 | if (mIndicatorHeight > 0) {
463 | mTrianglePaint.setColor(mIndicatorColor);
464 | mTrianglePath.reset();
465 | mTrianglePath.moveTo(paddingLeft + mIndicatorRect.left, height);
466 | mTrianglePath.lineTo(paddingLeft + mIndicatorRect.left / 2 + mIndicatorRect.right / 2, height - mIndicatorHeight);
467 | mTrianglePath.lineTo(paddingLeft + mIndicatorRect.right, height);
468 | mTrianglePath.close();
469 | canvas.drawPath(mTrianglePath, mTrianglePaint);
470 | }
471 | } else if (mIndicatorStyle == STYLE_BLOCK) {
472 | if (mIndicatorHeight < 0) {
473 | mIndicatorHeight = height - mIndicatorMarginTop - mIndicatorMarginBottom;
474 | } else {
475 |
476 | }
477 |
478 | if (mIndicatorHeight > 0) {
479 | if (mIndicatorCornerRadius < 0 || mIndicatorCornerRadius > mIndicatorHeight / 2) {
480 | mIndicatorCornerRadius = mIndicatorHeight / 2;
481 | }
482 |
483 | mIndicatorDrawable.setColor(mIndicatorColor);
484 | mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left,
485 | (int) mIndicatorMarginTop, (int) (paddingLeft + mIndicatorRect.right - mIndicatorMarginRight),
486 | (int) (mIndicatorMarginTop + mIndicatorHeight));
487 | mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius);
488 | mIndicatorDrawable.draw(canvas);
489 | }
490 | } else {
491 | /* mRectPaint.setColor(mIndicatorColor);
492 | calcIndicatorRect();
493 | canvas.drawRect(getPaddingLeft() + mIndicatorRect.left, getHeight() - mIndicatorHeight,
494 | mIndicatorRect.right + getPaddingLeft(), getHeight(), mRectPaint);*/
495 |
496 | if (mIndicatorHeight > 0) {
497 | mIndicatorDrawable.setColor(mIndicatorColor);
498 |
499 | if (mIndicatorGravity == Gravity.BOTTOM) {
500 | mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left,
501 | height - (int) mIndicatorHeight - (int) mIndicatorMarginBottom,
502 | paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight,
503 | height - (int) mIndicatorMarginBottom);
504 | } else {
505 | mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left,
506 | (int) mIndicatorMarginTop,
507 | paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight,
508 | (int) mIndicatorHeight + (int) mIndicatorMarginTop);
509 | }
510 | mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius);
511 | mIndicatorDrawable.draw(canvas);
512 | }
513 | }
514 | }
515 |
516 | //setter and getter
517 | public void setCurrentTab(int currentTab) {
518 | this.mCurrentTab = currentTab;
519 | mViewPager.setCurrentItem(currentTab);
520 | }
521 |
522 | public void setIndicatorStyle(int indicatorStyle) {
523 | this.mIndicatorStyle = indicatorStyle;
524 | invalidate();
525 | }
526 |
527 | public void setTabPadding(float tabPadding) {
528 | this.mTabPadding = dp2px(tabPadding);
529 | updateTabStyles();
530 | }
531 |
532 | public void setTabSpaceEqual(boolean tabSpaceEqual) {
533 | this.mTabSpaceEqual = tabSpaceEqual;
534 | updateTabStyles();
535 | }
536 |
537 | public void setTabWidth(float tabWidth) {
538 | this.mTabWidth = dp2px(tabWidth);
539 | updateTabStyles();
540 | }
541 |
542 | public void setIndicatorColor(int indicatorColor) {
543 | this.mIndicatorColor = indicatorColor;
544 | invalidate();
545 | }
546 |
547 | public void setIndicatorHeight(float indicatorHeight) {
548 | this.mIndicatorHeight = dp2px(indicatorHeight);
549 | invalidate();
550 | }
551 |
552 | public void setIndicatorWidth(float indicatorWidth) {
553 | this.mIndicatorWidth = dp2px(indicatorWidth);
554 | invalidate();
555 | }
556 |
557 | public void setIndicatorCornerRadius(float indicatorCornerRadius) {
558 | this.mIndicatorCornerRadius = dp2px(indicatorCornerRadius);
559 | invalidate();
560 | }
561 |
562 | public void setIndicatorGravity(int indicatorGravity) {
563 | this.mIndicatorGravity = indicatorGravity;
564 | invalidate();
565 | }
566 |
567 | public void setIndicatorMargin(float indicatorMarginLeft, float indicatorMarginTop,
568 | float indicatorMarginRight, float indicatorMarginBottom) {
569 | this.mIndicatorMarginLeft = dp2px(indicatorMarginLeft);
570 | this.mIndicatorMarginTop = dp2px(indicatorMarginTop);
571 | this.mIndicatorMarginRight = dp2px(indicatorMarginRight);
572 | this.mIndicatorMarginBottom = dp2px(indicatorMarginBottom);
573 | invalidate();
574 | }
575 |
576 | public void setIndicatorWidthEqualTitle(boolean indicatorWidthEqualTitle) {
577 | this.mIndicatorWidthEqualTitle = indicatorWidthEqualTitle;
578 | invalidate();
579 | }
580 |
581 | public void setUnderlineColor(int underlineColor) {
582 | this.mUnderlineColor = underlineColor;
583 | invalidate();
584 | }
585 |
586 | public void setUnderlineHeight(float underlineHeight) {
587 | this.mUnderlineHeight = dp2px(underlineHeight);
588 | invalidate();
589 | }
590 |
591 | public void setUnderlineGravity(int underlineGravity) {
592 | this.mUnderlineGravity = underlineGravity;
593 | invalidate();
594 | }
595 |
596 | public void setDividerColor(int dividerColor) {
597 | this.mDividerColor = dividerColor;
598 | invalidate();
599 | }
600 |
601 | public void setDividerWidth(float dividerWidth) {
602 | this.mDividerWidth = dp2px(dividerWidth);
603 | invalidate();
604 | }
605 |
606 | public void setDividerPadding(float dividerPadding) {
607 | this.mDividerPadding = dp2px(dividerPadding);
608 | invalidate();
609 | }
610 |
611 | public void setTextsize(float textsize) {
612 | this.mTextsize = sp2px(textsize);
613 | updateTabStyles();
614 | }
615 |
616 | public void setTextSelectColor(int textSelectColor) {
617 | this.mTextSelectColor = textSelectColor;
618 | updateTabStyles();
619 | }
620 |
621 | public void setTextUnselectColor(int textUnselectColor) {
622 | this.mTextUnselectColor = textUnselectColor;
623 | updateTabStyles();
624 | }
625 |
626 | public void setTextBold(boolean textBold) {
627 | this.mTextBold = textBold;
628 | updateTabStyles();
629 | }
630 |
631 | public void setTextAllCaps(boolean textAllCaps) {
632 | this.mTextAllCaps = textAllCaps;
633 | updateTabStyles();
634 | }
635 |
636 |
637 | public int getTabCount() {
638 | return mTabCount;
639 | }
640 |
641 | public int getCurrentTab() {
642 | return mCurrentTab;
643 | }
644 |
645 | public int getIndicatorStyle() {
646 | return mIndicatorStyle;
647 | }
648 |
649 | public float getTabPadding() {
650 | return mTabPadding;
651 | }
652 |
653 | public boolean isTabSpaceEqual() {
654 | return mTabSpaceEqual;
655 | }
656 |
657 | public float getTabWidth() {
658 | return mTabWidth;
659 | }
660 |
661 | public int getIndicatorColor() {
662 | return mIndicatorColor;
663 | }
664 |
665 | public float getIndicatorHeight() {
666 | return mIndicatorHeight;
667 | }
668 |
669 | public float getIndicatorWidth() {
670 | return mIndicatorWidth;
671 | }
672 |
673 | public float getIndicatorCornerRadius() {
674 | return mIndicatorCornerRadius;
675 | }
676 |
677 | public float getIndicatorMarginLeft() {
678 | return mIndicatorMarginLeft;
679 | }
680 |
681 | public float getIndicatorMarginTop() {
682 | return mIndicatorMarginTop;
683 | }
684 |
685 | public float getIndicatorMarginRight() {
686 | return mIndicatorMarginRight;
687 | }
688 |
689 | public float getIndicatorMarginBottom() {
690 | return mIndicatorMarginBottom;
691 | }
692 |
693 | public int getUnderlineColor() {
694 | return mUnderlineColor;
695 | }
696 |
697 | public float getUnderlineHeight() {
698 | return mUnderlineHeight;
699 | }
700 |
701 | public int getDividerColor() {
702 | return mDividerColor;
703 | }
704 |
705 | public float getDividerWidth() {
706 | return mDividerWidth;
707 | }
708 |
709 | public float getDividerPadding() {
710 | return mDividerPadding;
711 | }
712 |
713 | public float getTextsize() {
714 | return mTextsize;
715 | }
716 |
717 | public int getTextSelectColor() {
718 | return mTextSelectColor;
719 | }
720 |
721 | public int getTextUnselectColor() {
722 | return mTextUnselectColor;
723 | }
724 |
725 | public boolean isTextBold() {
726 | return mTextBold;
727 | }
728 |
729 | public boolean isTextAllCaps() {
730 | return mTextAllCaps;
731 | }
732 |
733 | public TextView getTitleView(int tab) {
734 | View tabView = mTabsContainer.getChildAt(tab);
735 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
736 | return tv_tab_title;
737 | }
738 |
739 | //setter and getter
740 |
741 | // show MsgTipView
742 | private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
743 | private SparseArray mInitSetMap = new SparseArray<>();
744 |
745 | /**
746 | * 显示未读消息
747 | *
748 | * @param position 显示tab位置
749 | * @param num num小于等于0显示红点,num大于0显示数字
750 | */
751 | public void showMsg(int position, int num) {
752 | if (position >= mTabCount) {
753 | position = mTabCount - 1;
754 | }
755 |
756 | View tabView = mTabsContainer.getChildAt(position);
757 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
758 | if (tipView != null) {
759 | UnreadMsgUtils.show(tipView, num);
760 |
761 | if (mInitSetMap.get(position) != null && mInitSetMap.get(position)) {
762 | return;
763 | }
764 |
765 | setMsgMargin(position, 4, 2);
766 | mInitSetMap.put(position, true);
767 | }
768 | }
769 |
770 | /**
771 | * 显示未读红点
772 | *
773 | * @param position 显示tab位置
774 | */
775 | public void showDot(int position) {
776 | if (position >= mTabCount) {
777 | position = mTabCount - 1;
778 | }
779 | showMsg(position, 0);
780 | }
781 |
782 | /** 隐藏未读消息 */
783 | public void hideMsg(int position) {
784 | if (position >= mTabCount) {
785 | position = mTabCount - 1;
786 | }
787 |
788 | View tabView = mTabsContainer.getChildAt(position);
789 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
790 |
791 | if (tipView != null) {
792 | tipView.setVisibility(View.GONE);
793 | }
794 | }
795 |
796 | /** 设置未读消息偏移,原点为文字的右上角.当控件高度固定,消息提示位置易控制,显示效果佳 */
797 | public void setMsgMargin(int position, float leftPadding, float bottomPadding) {
798 | if (position >= mTabCount) {
799 | position = mTabCount - 1;
800 | }
801 | View tabView = mTabsContainer.getChildAt(position);
802 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
803 | if (tipView != null) {
804 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
805 | mTextPaint.setTextSize(mTextsize);
806 | float textWidth = mTextPaint.measureText(tv_tab_title.getText().toString());
807 | float textHeight = mTextPaint.descent() - mTextPaint.ascent();
808 | MarginLayoutParams lp = (MarginLayoutParams) tipView.getLayoutParams();
809 | lp.leftMargin = mTabWidth >= 0 ? (int) (mTabWidth / 2 + textWidth / 2 + dp2px(leftPadding)) : (int) (mTabPadding + textWidth + dp2px(leftPadding));
810 | lp.topMargin = mHeight > 0 ? (int) (mHeight - textHeight) / 2 - dp2px(bottomPadding) : 0;
811 | tipView.setLayoutParams(lp);
812 | }
813 | }
814 |
815 | /** 当前类只提供了少许设置未读消息属性的方法,可以通过该方法获取MsgView对象从而各种设置 */
816 | public MsgView getMsgView(int position) {
817 | if (position >= mTabCount) {
818 | position = mTabCount - 1;
819 | }
820 | View tabView = mTabsContainer.getChildAt(position);
821 | MsgView tipView = (MsgView) tabView.findViewById(R.id.rtv_msg_tip);
822 | return tipView;
823 | }
824 |
825 | private OnTabSelectListener mListener;
826 |
827 | public void setOnTabSelectListener(OnTabSelectListener listener) {
828 | this.mListener = listener;
829 | }
830 |
831 | class InnerPagerAdapter extends FragmentPagerAdapter {
832 | private ArrayList fragments = new ArrayList<>();
833 | private String[] titles;
834 |
835 | public InnerPagerAdapter(FragmentManager fm, ArrayList fragments, String[] titles) {
836 | super(fm);
837 | this.fragments = fragments;
838 | this.titles = titles;
839 | }
840 |
841 | @Override
842 | public int getCount() {
843 | return fragments.size();
844 | }
845 |
846 | @Override
847 | public CharSequence getPageTitle(int position) {
848 | return titles[position];
849 | }
850 |
851 | @Override
852 | public Fragment getItem(int position) {
853 | return fragments.get(position);
854 | }
855 |
856 | @Override
857 | public void destroyItem(ViewGroup container, int position, Object object) {
858 | // 覆写destroyItem并且空实现,这样每个Fragment中的视图就不会被销毁
859 | // super.destroyItem(container, position, object);
860 | }
861 |
862 | @Override
863 | public int getItemPosition(Object object) {
864 | return PagerAdapter.POSITION_NONE;
865 | }
866 | }
867 |
868 | @Override
869 | protected Parcelable onSaveInstanceState() {
870 | Bundle bundle = new Bundle();
871 | bundle.putParcelable("instanceState", super.onSaveInstanceState());
872 | bundle.putInt("mCurrentTab", mCurrentTab);
873 | return bundle;
874 | }
875 |
876 | @Override
877 | protected void onRestoreInstanceState(Parcelable state) {
878 | if (state instanceof Bundle) {
879 | Bundle bundle = (Bundle) state;
880 | mCurrentTab = bundle.getInt("mCurrentTab");
881 | state = bundle.getParcelable("instanceState");
882 | if (mCurrentTab != 0 && mTabsContainer.getChildCount() > 0) {
883 | updateTabSelection(mCurrentTab);
884 | scrollToCurrentTab();
885 | }
886 | }
887 | super.onRestoreInstanceState(state);
888 | }
889 |
890 | protected int dp2px(float dp) {
891 | final float scale = mContext.getResources().getDisplayMetrics().density;
892 | return (int) (dp * scale + 0.5f);
893 | }
894 |
895 | protected int sp2px(float sp) {
896 | final float scale = this.mContext.getResources().getDisplayMetrics().scaledDensity;
897 | return (int) (sp * scale + 0.5f);
898 | }
899 | }
900 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/java/com/flyco/tablayout/listener/CustomTabEntity.java:
--------------------------------------------------------------------------------
1 | package com.flyco.tablayout.listener;
2 |
3 | import android.support.annotation.DrawableRes;
4 |
5 | public interface CustomTabEntity {
6 | String getTabTitle();
7 |
8 | @DrawableRes
9 | int getTabSelectedIcon();
10 |
11 | @DrawableRes
12 | int getTabUnselectedIcon();
13 |
14 | String getSubTitle();
15 | }
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/java/com/flyco/tablayout/listener/OnTabSelectListener.java:
--------------------------------------------------------------------------------
1 | package com.flyco.tablayout.listener;
2 |
3 | public interface OnTabSelectListener {
4 | void onTabSelect(int position);
5 | void onTabReselect(int position);
6 | }
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/java/com/flyco/tablayout/utils/FragmentChangeManager.java:
--------------------------------------------------------------------------------
1 | package com.flyco.tablayout.utils;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentTransaction;
6 |
7 | import java.util.ArrayList;
8 |
9 | public class FragmentChangeManager {
10 | private FragmentManager mFragmentManager;
11 | private int mContainerViewId;
12 | /** Fragment切换数组 */
13 | private ArrayList mFragments;
14 | /** 当前选中的Tab */
15 | private int mCurrentTab;
16 |
17 | public FragmentChangeManager(FragmentManager fm, int containerViewId, ArrayList fragments) {
18 | this.mFragmentManager = fm;
19 | this.mContainerViewId = containerViewId;
20 | this.mFragments = fragments;
21 | initFragments();
22 | }
23 |
24 | /** 初始化fragments */
25 | private void initFragments() {
26 | for (Fragment fragment : mFragments) {
27 | mFragmentManager.beginTransaction().add(mContainerViewId, fragment).hide(fragment).commit();
28 | }
29 |
30 | setFragments(0);
31 | }
32 |
33 | /** 界面切换控制 */
34 | public void setFragments(int index) {
35 | for (int i = 0; i < mFragments.size(); i++) {
36 | FragmentTransaction ft = mFragmentManager.beginTransaction();
37 | Fragment fragment = mFragments.get(i);
38 | if (i == index) {
39 | ft.show(fragment);
40 | } else {
41 | ft.hide(fragment);
42 | }
43 | ft.commit();
44 | }
45 | mCurrentTab = index;
46 | }
47 |
48 | public int getCurrentTab() {
49 | return mCurrentTab;
50 | }
51 |
52 | public Fragment getCurrentFragment() {
53 | return mFragments.get(mCurrentTab);
54 | }
55 | }
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/java/com/flyco/tablayout/utils/UnreadMsgUtils.java:
--------------------------------------------------------------------------------
1 | package com.flyco.tablayout.utils;
2 |
3 |
4 | import android.util.DisplayMetrics;
5 | import android.view.View;
6 | import android.widget.FrameLayout;
7 | import android.widget.LinearLayout;
8 | import android.widget.RelativeLayout;
9 |
10 | import com.flyco.tablayout.widget.MsgView;
11 |
12 | /**
13 | * 未读消息提示View,显示小红点或者带有数字的红点:
14 | * 数字一位,圆
15 | * 数字两位,圆角矩形,圆角是高度的一半
16 | * 数字超过两位,显示99+
17 | */
18 | public class UnreadMsgUtils {
19 | public static void show(MsgView msgView, int num) {
20 | if (msgView == null) {
21 | return;
22 | }
23 | RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) msgView.getLayoutParams();
24 | DisplayMetrics dm = msgView.getResources().getDisplayMetrics();
25 | msgView.setVisibility(View.VISIBLE);
26 | if (num <= 0) {//圆点,设置默认宽高
27 | msgView.setStrokeWidth(0);
28 | msgView.setText("");
29 |
30 | lp.width = (int) (8 * dm.density);
31 | lp.height = (int) (8 * dm.density);
32 | msgView.setLayoutParams(lp);
33 | } else {
34 | lp.height = (int) (18 * dm.density);
35 | if (num > 0 && num < 10) {//圆
36 | lp.width = (int) (18 * dm.density);
37 | msgView.setText(num + "");
38 | } else if (num > 9 && num < 100) {//圆角矩形,圆角是高度的一半,设置默认padding
39 | lp.width = RelativeLayout.LayoutParams.WRAP_CONTENT;
40 | msgView.setPadding((int) (6 * dm.density), 0, (int) (6 * dm.density), 0);
41 | msgView.setText(num + "");
42 | } else {//数字超过两位,显示99+
43 | lp.width = RelativeLayout.LayoutParams.WRAP_CONTENT;
44 | msgView.setPadding((int) (6 * dm.density), 0, (int) (6 * dm.density), 0);
45 | msgView.setText("99+");
46 | }
47 | msgView.setLayoutParams(lp);
48 | }
49 | }
50 |
51 | public static void showRight(MsgView msgView, int num) {
52 | if (msgView == null) {
53 | return;
54 | }
55 | RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) msgView.getLayoutParams();
56 | DisplayMetrics dm = msgView.getResources().getDisplayMetrics();
57 | msgView.setVisibility(View.VISIBLE);
58 | if (num <= 0) {//圆点,设置默认宽高
59 | msgView.setStrokeWidth(0);
60 | msgView.setText("");
61 | lp.width = (int) (8 * dm.density);
62 | lp.height = (int) (8 * dm.density);
63 | msgView.setLayoutParams(lp);
64 | msgView.setVisibility(View.INVISIBLE);
65 | } else {
66 | lp.height = (int) (18 * dm.density);
67 | if (num > 0 && num < 10) {//圆
68 | lp.width = (int) (18 * dm.density);
69 | msgView.setPadding(0, 0, 0, 0);
70 | msgView.setText(num + "");
71 | } else if (num > 9 && num < 100) {//圆角矩形,圆角是高度的一半,设置默认padding
72 | lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
73 | msgView.setPadding((int) (6 * dm.density), -1, (int) (6 * dm.density), 0);
74 | msgView.setText(num + "");
75 | } else {//数字超过两位,显示99+
76 | lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
77 | msgView.setPadding((int) (6 * dm.density), 0, (int) (6 * dm.density), 0);
78 | msgView.setText("99+");
79 | }
80 | msgView.setLayoutParams(lp);
81 |
82 | }
83 | }
84 | public static void setSize(MsgView rtv, int size) {
85 | if (rtv == null) {
86 | return;
87 | }
88 | RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) rtv.getLayoutParams();
89 | lp.width = size;
90 | lp.height = size;
91 | rtv.setLayoutParams(lp);
92 | }
93 |
94 | public static void showPoint(MsgView msgView, int num) {
95 | if (msgView == null) {
96 | return;
97 | }
98 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) msgView.getLayoutParams();
99 | DisplayMetrics dm = msgView.getResources().getDisplayMetrics();
100 | msgView.setVisibility(View.VISIBLE);
101 | if (num <= 0) {//圆点,设置默认宽高
102 | msgView.setStrokeWidth(0);
103 | msgView.setText("");
104 | lp.width = (int) (8 * dm.density);
105 | lp.height = (int) (8 * dm.density);
106 | msgView.setLayoutParams(lp);
107 | msgView.setVisibility(View.GONE);
108 | } else {
109 | lp.height = (int) (18 * dm.density);
110 | if (num > 0 && num < 10) {//圆
111 | lp.width = (int) (18 * dm.density);
112 | msgView.setPadding(0, 0, 0, 0);
113 | msgView.setText(num + "");
114 | } else if (num > 9 && num < 100) {//圆角矩形,圆角是高度的一半,设置默认padding
115 | lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
116 | msgView.setPadding((int) (6 * dm.density), -1, (int) (6 * dm.density), 0);
117 | msgView.setText(num + "");
118 | } else {//数字超过两位,显示99+
119 | lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
120 | msgView.setPadding((int) (6 * dm.density), 0, (int) (6 * dm.density), 0);
121 | msgView.setText("99+");
122 | }
123 | msgView.setLayoutParams(lp);
124 |
125 | }
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/java/com/flyco/tablayout/widget/MsgView.java:
--------------------------------------------------------------------------------
1 | package com.flyco.tablayout.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Color;
6 | import android.graphics.drawable.GradientDrawable;
7 | import android.graphics.drawable.StateListDrawable;
8 | import android.os.Build;
9 | import android.util.AttributeSet;
10 | import android.widget.TextView;
11 |
12 | import com.flyco.tablayout.R;
13 |
14 | /** 用于需要圆角矩形框背景的TextView的情况,减少直接使用TextView时引入的shape资源文件 */
15 | public class MsgView extends TextView {
16 | private Context context;
17 | private GradientDrawable gd_background = new GradientDrawable();
18 | private int backgroundColor;
19 | private int cornerRadius;
20 | private int strokeWidth;
21 | private int strokeColor;
22 | private boolean isRadiusHalfHeight;
23 | private boolean isWidthHeightEqual;
24 |
25 | public MsgView(Context context) {
26 | this(context, null);
27 | }
28 |
29 | public MsgView(Context context, AttributeSet attrs) {
30 | this(context, attrs, 0);
31 | }
32 |
33 | public MsgView(Context context, AttributeSet attrs, int defStyleAttr) {
34 | super(context, attrs, defStyleAttr);
35 | this.context = context;
36 | obtainAttributes(context, attrs);
37 | }
38 |
39 |
40 | private void obtainAttributes(Context context, AttributeSet attrs) {
41 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MsgView);
42 | backgroundColor = ta.getColor(R.styleable.MsgView_mv_backgroundColor, Color.TRANSPARENT);
43 | cornerRadius = ta.getDimensionPixelSize(R.styleable.MsgView_mv_cornerRadius, 0);
44 | strokeWidth = ta.getDimensionPixelSize(R.styleable.MsgView_mv_strokeWidth, 0);
45 | strokeColor = ta.getColor(R.styleable.MsgView_mv_strokeColor, Color.TRANSPARENT);
46 | isRadiusHalfHeight = ta.getBoolean(R.styleable.MsgView_mv_isRadiusHalfHeight, false);
47 | isWidthHeightEqual = ta.getBoolean(R.styleable.MsgView_mv_isWidthHeightEqual, false);
48 |
49 | ta.recycle();
50 | }
51 |
52 | @Override
53 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
54 | if (isWidthHeightEqual() && getWidth() > 0 && getHeight() > 0) {
55 | int max = Math.max(getWidth(), getHeight());
56 | int measureSpec = MeasureSpec.makeMeasureSpec(max, MeasureSpec.EXACTLY);
57 | super.onMeasure(measureSpec, measureSpec);
58 | return;
59 | }
60 |
61 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
62 | }
63 |
64 | @Override
65 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
66 | super.onLayout(changed, left, top, right, bottom);
67 | if (isRadiusHalfHeight()) {
68 | setCornerRadius(getHeight() / 2);
69 | } else {
70 | setBgSelector();
71 | }
72 | }
73 |
74 |
75 | public void setBackgroundColor(int backgroundColor) {
76 | this.backgroundColor = backgroundColor;
77 | setBgSelector();
78 | }
79 |
80 | public void setCornerRadius(int cornerRadius) {
81 | this.cornerRadius = dp2px(cornerRadius);
82 | setBgSelector();
83 | }
84 |
85 | public void setStrokeWidth(int strokeWidth) {
86 | this.strokeWidth = dp2px(strokeWidth);
87 | setBgSelector();
88 | }
89 |
90 | public void setStrokeColor(int strokeColor) {
91 | this.strokeColor = strokeColor;
92 | setBgSelector();
93 | }
94 |
95 | public void setIsRadiusHalfHeight(boolean isRadiusHalfHeight) {
96 | this.isRadiusHalfHeight = isRadiusHalfHeight;
97 | setBgSelector();
98 | }
99 |
100 | public void setIsWidthHeightEqual(boolean isWidthHeightEqual) {
101 | this.isWidthHeightEqual = isWidthHeightEqual;
102 | setBgSelector();
103 | }
104 |
105 | public int getBackgroundColor() {
106 | return backgroundColor;
107 | }
108 |
109 | public int getCornerRadius() {
110 | return cornerRadius;
111 | }
112 |
113 | public int getStrokeWidth() {
114 | return strokeWidth;
115 | }
116 |
117 | public int getStrokeColor() {
118 | return strokeColor;
119 | }
120 |
121 | public boolean isRadiusHalfHeight() {
122 | return isRadiusHalfHeight;
123 | }
124 |
125 | public boolean isWidthHeightEqual() {
126 | return isWidthHeightEqual;
127 | }
128 |
129 | protected int dp2px(float dp) {
130 | final float scale = context.getResources().getDisplayMetrics().density;
131 | return (int) (dp * scale + 0.5f);
132 | }
133 |
134 | protected int sp2px(float sp) {
135 | final float scale = this.context.getResources().getDisplayMetrics().scaledDensity;
136 | return (int) (sp * scale + 0.5f);
137 | }
138 |
139 | private void setDrawable(GradientDrawable gd, int color, int strokeColor) {
140 | gd.setColor(color);
141 | gd.setCornerRadius(cornerRadius);
142 | gd.setStroke(strokeWidth, strokeColor);
143 | }
144 |
145 | public void setBgSelector() {
146 | StateListDrawable bg = new StateListDrawable();
147 |
148 | setDrawable(gd_background, backgroundColor, strokeColor);
149 | bg.addState(new int[]{-android.R.attr.state_pressed}, gd_background);
150 |
151 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {//16
152 | setBackground(bg);
153 | } else {
154 | //noinspection deprecation
155 | setBackgroundDrawable(bg);
156 | }
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/res/layout/layout_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
21 |
22 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/res/layout/layout_tab_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
22 |
27 |
31 |
32 |
33 |
34 |
48 |
49 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/res/layout/layout_tab_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
20 |
21 |
26 |
27 |
32 |
33 |
34 |
47 |
48 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/res/layout/layout_tab_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
22 |
27 |
31 |
32 |
33 |
34 |
48 |
49 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/res/layout/layout_tab_segment.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
21 |
22 |
27 |
28 |
29 |
42 |
43 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/src/main/res/layout/layout_tab_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
21 |
22 |
27 |
28 |
33 |
34 |
35 |
49 |
50 |
--------------------------------------------------------------------------------
/tabLayoutLibrary/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 |
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 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------