feat : 重写 装扮商城 , 我的装扮 , 装扮赠送 页面
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package com.chwl.library.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.collection.LruCache;
|
||||
|
||||
/**
|
||||
* @author Rowand jj
|
||||
*
|
||||
*ʹ<><CAB9>Lrucache<68><65><EFBFBD><EFBFBD>bitmap<61>Ĺ<EFBFBD><C4B9><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
public class BitmapLruCacheHelper
|
||||
{
|
||||
private static final String TAG = "BitmapLruCacheHelper";
|
||||
private static BitmapLruCacheHelper instance = new BitmapLruCacheHelper();
|
||||
LruCache<String, Bitmap> cache = null;
|
||||
//<2F><><EFBFBD><EFBFBD>
|
||||
private BitmapLruCacheHelper()
|
||||
{
|
||||
int maxSize = (int) (Runtime.getRuntime().maxMemory() / (float) 8);
|
||||
cache = new LruCache<String, Bitmap>(maxSize)
|
||||
{
|
||||
@Override
|
||||
protected int sizeOf(String key, Bitmap value)
|
||||
{
|
||||
return value.getRowBytes()*value.getHeight();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static BitmapLruCacheHelper getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
/**
|
||||
*<2A><><EFBFBD>뻺<EFBFBD><EBBBBA>
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void addBitmapToMemCache(String key, Bitmap value)
|
||||
{
|
||||
if(key == null || value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(cache!=null && getBitmapFromMemCache(key)==null)
|
||||
{
|
||||
cache.put(key, value);
|
||||
Log.i(TAG,"put bitmap to lrucache success");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <20>ӻ<EFBFBD><D3BB><EFBFBD><EFBFBD>л<EFBFBD>ȡͼƬ
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Bitmap getBitmapFromMemCache(String key)
|
||||
{
|
||||
if(key == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Bitmap bitmap = cache.get(key);
|
||||
Log.i(TAG,"get bitmap from lrucache,bitmap="+bitmap);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>ָ<EFBFBD><D6B8>bitmap<61>ӻ<EFBFBD><D3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƴ<EFBFBD>
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Bitmap removeBitmapFromMemCache(String key)
|
||||
{
|
||||
if(key == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return cache.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,8 +1,15 @@
|
||||
package com.chwl.library.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.drawable.AnimationDrawable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.util.LruCache
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
@@ -76,7 +83,7 @@ class SVGAView : SVGAImageView, ILog {
|
||||
}
|
||||
|
||||
fun loadUrl(url: String?,autoPayer:Boolean = true) {
|
||||
logD("loadUrl() url:$url")
|
||||
logD("SVGAView loadUrl() url:$url")
|
||||
if (url.isNullOrEmpty()) {
|
||||
this.resourceUrl = null
|
||||
this.setImageDrawable(null)
|
||||
@@ -84,17 +91,8 @@ class SVGAView : SVGAImageView, ILog {
|
||||
return
|
||||
}
|
||||
|
||||
if (!url.endsWith(".svga")) {
|
||||
this.resourceUrl = null
|
||||
this.setImageDrawable(null)
|
||||
onViewStateChanged(0)
|
||||
GlideUtils.instance().load(url,this)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (url == resourceUrl && drawable is SVGADrawable) {
|
||||
logD("loadUrl() 已加载 isAnimating:$isAnimating")
|
||||
logD("SVGAView loadUrl() 已加载 isAnimating:$isAnimating")
|
||||
if (!isAnimating) {
|
||||
if (autoPayer) {
|
||||
startAnimation()
|
||||
@@ -107,7 +105,7 @@ class SVGAView : SVGAImageView, ILog {
|
||||
this.resourceUrl = url
|
||||
val cacheItem = svgaCache?.get(url)
|
||||
if (cacheItem != null) {
|
||||
logD("loadUrl() 有缓存")
|
||||
logD("SVGAView loadUrl() 有缓存")
|
||||
this@SVGAView.setImageDrawable(SVGADrawable(cacheItem))
|
||||
if (autoPayer) {
|
||||
this@SVGAView.startAnimation()
|
||||
@@ -118,15 +116,15 @@ class SVGAView : SVGAImageView, ILog {
|
||||
}
|
||||
|
||||
private fun loadSVGAUrl(url: String,autoPayer:Boolean = true) {
|
||||
logD("loadSVGAUrl url:$url")
|
||||
GlideUtils.instance().downloadFromUrl(context,url,object : RequestListener<File?> {
|
||||
logD("SVGAView loadSVGAUrl url:$url")
|
||||
GlideUtils.instance().downloadFromUrl2(context,url,object : RequestListener<File?> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<File?>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
logD("loadSVGAUrl onDownloadError url:$url")
|
||||
logD("SVGAView loadSVGAUrl onDownloadError url:$url")
|
||||
if (resourceUrl == url) {
|
||||
onViewStateChanged(-1)
|
||||
}
|
||||
@@ -142,20 +140,112 @@ class SVGAView : SVGAImageView, ILog {
|
||||
): Boolean {
|
||||
if (resource != null) {
|
||||
val path = resource.path
|
||||
logD("loadSVGAUrl onDownloadCompleted url:$url path:$path")
|
||||
if (resourceUrl == url) {
|
||||
loadSVGAFile(url, path,autoPayer)
|
||||
var isImg = false
|
||||
var outMimeType = "null"
|
||||
try {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
val decodeFile = BitmapFactory.decodeFile(path, options)
|
||||
outMimeType = options.outMimeType
|
||||
isImg = true
|
||||
|
||||
} catch (e: Exception) {
|
||||
isImg = false
|
||||
}
|
||||
|
||||
logD("SVGAView loadSVGAUrl onDownloadCompleted url:$url isImg=$isImg outMimeType=$outMimeType path:$path")
|
||||
if (!isImg) {
|
||||
loadSVGAFile(url, path, autoPayer)
|
||||
} else {
|
||||
this@SVGAView.post {
|
||||
this@SVGAView.resourceUrl = null
|
||||
onViewStateChanged(0)
|
||||
loadImage(url)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadImage(url: String) {
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.dontAnimate()
|
||||
.dontTransform()
|
||||
.load(url)
|
||||
.listener(object : RequestListener<Bitmap?> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any,
|
||||
target: Target<Bitmap?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
this@SVGAView.resourceUrl = null
|
||||
onViewStateChanged(0)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap?,
|
||||
model: Any,
|
||||
target: Target<Bitmap?>,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
if (resource == null) return false
|
||||
val split: List<Drawable> = split(resource)
|
||||
val animationDrawable = AnimationDrawable()
|
||||
for (i in split.indices) {
|
||||
animationDrawable.addFrame(split[i], 200)
|
||||
}
|
||||
this@SVGAView.post(Runnable {
|
||||
this@SVGAView.setImageDrawable(animationDrawable)
|
||||
animationDrawable.isOneShot = false
|
||||
animationDrawable.start()
|
||||
})
|
||||
return false
|
||||
}
|
||||
}).submit()
|
||||
}
|
||||
|
||||
fun split(bitmap: Bitmap?): List<Drawable> {
|
||||
val pieces: MutableList<Drawable> = ArrayList()
|
||||
try {
|
||||
if (bitmap!!.width == bitmap.height) {
|
||||
pieces.add(BitmapDrawable(bitmap))
|
||||
} else {
|
||||
val matrix = Matrix()
|
||||
matrix.setScale(0.5f, 0.5f)
|
||||
|
||||
val width = bitmap.width
|
||||
val pieceWidth = bitmap.height
|
||||
val pieceHeight = bitmap.height
|
||||
val xPiece = width / pieceWidth
|
||||
for (j in 0 until xPiece) {
|
||||
val xValue = j * pieceWidth
|
||||
pieces.add(
|
||||
BitmapDrawable(
|
||||
Bitmap.createBitmap(
|
||||
bitmap, xValue, 0,
|
||||
pieceWidth, pieceHeight, matrix, true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: java.lang.Exception) {
|
||||
}
|
||||
return pieces
|
||||
}
|
||||
|
||||
private fun loadSVGAFile(url: String, path: String,autoPayer:Boolean = true) {
|
||||
try {
|
||||
logD("loadSVGAFile path:$path url:$url")
|
||||
logD("SVGAView loadSVGAFile path:$path url:$url")
|
||||
val inputStream = BufferedInputStream(FileInputStream(path))
|
||||
shareParser().decodeFromInputStream(
|
||||
inputStream,
|
||||
@@ -188,7 +278,7 @@ class SVGAView : SVGAImageView, ILog {
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
logD("loadSVGAFile url:$url e:${e.message}")
|
||||
logD("SVGAView loadSVGAFile url:$url e:${e.message}")
|
||||
onViewStateChanged(-1)
|
||||
}
|
||||
}
|
||||
@@ -197,16 +287,16 @@ class SVGAView : SVGAImageView, ILog {
|
||||
* @param state -1 异常、0 空、1 成功
|
||||
*/
|
||||
private fun onViewStateChanged(state: Int) {
|
||||
logD("onViewStateChanged state:$state")
|
||||
logD("SVGAView onViewStateChanged state:$state")
|
||||
}
|
||||
|
||||
fun bindCache(cache: SVGACache?) {
|
||||
logD("bindCache() cache:$cache")
|
||||
logD("SVGAView bindCache() cache:$cache")
|
||||
this.svgaCache = cache
|
||||
}
|
||||
|
||||
fun setLogTag(tag: String) {
|
||||
logD("setLogTag() newTag:$tag oldTag:$logTag")
|
||||
logD("SVGAView setLogTag() newTag:$tag oldTag:$logTag")
|
||||
this.logTag = tag
|
||||
}
|
||||
|
||||
@@ -219,23 +309,25 @@ class SVGAView : SVGAImageView, ILog {
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
logD("onDetachedFromWindow()")
|
||||
logD("SVGAView onDetachedFromWindow()")
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
logD("onAttachedToWindow()")
|
||||
logD("SVGAView onAttachedToWindow()")
|
||||
super.onAttachedToWindow()
|
||||
if (resumePlayAfterAttached) {
|
||||
if (drawable is SVGADrawable) {
|
||||
if (!isAnimating) {
|
||||
logD("onAttachedToWindow() startAnimation")
|
||||
logD("SVGAView onAttachedToWindow() startAnimation")
|
||||
startAnimation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface SVGACache {
|
||||
fun get(key: String): SVGAVideoEntity?
|
||||
|
||||
|
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab;
|
||||
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
public abstract class SmartTabIndicationInterpolator {
|
||||
|
||||
public static final SmartTabIndicationInterpolator SMART = new SmartIndicationInterpolator();
|
||||
public static final SmartTabIndicationInterpolator LINEAR = new LinearIndicationInterpolator();
|
||||
|
||||
static final int ID_SMART = 0;
|
||||
static final int ID_LINEAR = 1;
|
||||
|
||||
public static SmartTabIndicationInterpolator of(int id) {
|
||||
switch (id) {
|
||||
case ID_SMART:
|
||||
return SMART;
|
||||
case ID_LINEAR:
|
||||
return LINEAR;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract float getLeftEdge(float offset);
|
||||
|
||||
public abstract float getRightEdge(float offset);
|
||||
|
||||
public float getThickness(float offset) {
|
||||
return 1f; //Always the same thickness by default
|
||||
}
|
||||
|
||||
public static class SmartIndicationInterpolator extends SmartTabIndicationInterpolator {
|
||||
|
||||
private static final float DEFAULT_INDICATOR_INTERPOLATION_FACTOR = 3.0f;
|
||||
|
||||
private final Interpolator leftEdgeInterpolator;
|
||||
private final Interpolator rightEdgeInterpolator;
|
||||
|
||||
public SmartIndicationInterpolator() {
|
||||
this(DEFAULT_INDICATOR_INTERPOLATION_FACTOR);
|
||||
}
|
||||
|
||||
public SmartIndicationInterpolator(float factor) {
|
||||
leftEdgeInterpolator = new AccelerateInterpolator(factor);
|
||||
rightEdgeInterpolator = new DecelerateInterpolator(factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getLeftEdge(float offset) {
|
||||
return leftEdgeInterpolator.getInterpolation(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRightEdge(float offset) {
|
||||
return rightEdgeInterpolator.getInterpolation(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getThickness(float offset) {
|
||||
return 1f / (1.0f - getLeftEdge(offset) + getRightEdge(offset));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class LinearIndicationInterpolator extends SmartTabIndicationInterpolator {
|
||||
|
||||
@Override
|
||||
public float getLeftEdge(float offset) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRightEdge(float offset) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,644 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.chwl.library.R;
|
||||
|
||||
/**
|
||||
* To be used with ViewPager to provide a tab indicator component which give constant feedback as
|
||||
* to
|
||||
* the user's scroll progress.
|
||||
* <p>
|
||||
* To use the component, simply add it to your view hierarchy. Then in your
|
||||
* {@link android.app.Activity} or {@link androidx.fragment.app.Fragment} call
|
||||
* {@link #setViewPager(ViewPager)} providing it the ViewPager this
|
||||
* layout
|
||||
* is being used for.
|
||||
* <p>
|
||||
* The colors can be customized in two ways. The first and simplest is to provide an array of
|
||||
* colors
|
||||
* via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
|
||||
* alternative is via the {@link TabColorizer} interface which provides you complete control over
|
||||
* which color is used for any individual position.
|
||||
* <p>
|
||||
* The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
|
||||
* providing the layout ID of your custom layout.
|
||||
* <p>
|
||||
* Forked from Google Samples > SlidingTabsBasic >
|
||||
* <a href="https://developer.android.com/samples/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.html">SlidingTabLayout</a>
|
||||
*/
|
||||
public class SmartTabLayout extends HorizontalScrollView {
|
||||
|
||||
private static final boolean DEFAULT_DISTRIBUTE_EVENLY = false;
|
||||
private static final int TITLE_OFFSET_DIPS = 24;
|
||||
private static final int TITLE_OFFSET_AUTO_CENTER = -1;
|
||||
private static final int TAB_VIEW_PADDING_DIPS = 16;
|
||||
private static final boolean TAB_VIEW_TEXT_ALL_CAPS = true;
|
||||
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
|
||||
private static final int TAB_VIEW_TEXT_COLOR = 0xFC000000;
|
||||
private static final int TAB_VIEW_TEXT_MIN_WIDTH = 0;
|
||||
private static final boolean TAB_CLICKABLE = true;
|
||||
private static final int NO_TEXT_STYLE = -1;
|
||||
|
||||
protected final SmartTabStrip tabStrip;
|
||||
private int titleOffset;
|
||||
private int tabViewBackgroundResId;
|
||||
private boolean tabViewTextAllCaps;
|
||||
private ColorStateList tabViewTextColors;
|
||||
private float tabViewTextSize;
|
||||
private int tabViewTextHorizontalPadding;
|
||||
private int tabViewTextMinWidth;
|
||||
private ViewPager viewPager;
|
||||
private ViewPager.OnPageChangeListener viewPagerPageChangeListener;
|
||||
private OnScrollChangeListener onScrollChangeListener;
|
||||
private TabProvider tabProvider;
|
||||
private InternalTabClickListener internalTabClickListener;
|
||||
private OnTabClickListener onTabClickListener;
|
||||
private boolean distributeEvenly;
|
||||
private int textAppearance;
|
||||
|
||||
public SmartTabLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SmartTabLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SmartTabLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
// Disable the Scroll Bar
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
|
||||
final DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
final float density = dm.density;
|
||||
|
||||
int tabBackgroundResId = NO_ID;
|
||||
boolean textAllCaps = TAB_VIEW_TEXT_ALL_CAPS;
|
||||
ColorStateList textColors;
|
||||
float textSize = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP, dm);
|
||||
int textHorizontalPadding = (int) (TAB_VIEW_PADDING_DIPS * density);
|
||||
int textMinWidth = (int) (TAB_VIEW_TEXT_MIN_WIDTH * density);
|
||||
boolean distributeEvenly = DEFAULT_DISTRIBUTE_EVENLY;
|
||||
int customTabLayoutId = NO_ID;
|
||||
int customTabTextViewId = NO_ID;
|
||||
boolean clickable = TAB_CLICKABLE;
|
||||
int titleOffset = (int) (TITLE_OFFSET_DIPS * density);
|
||||
int textStyle = NO_TEXT_STYLE;
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.stl_SmartTabLayout, defStyle, 0);
|
||||
tabBackgroundResId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabBackground, tabBackgroundResId);
|
||||
textAllCaps = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextAllCaps, textAllCaps);
|
||||
textColors = a.getColorStateList(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextColor);
|
||||
textSize = a.getDimension(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextSize, textSize);
|
||||
textHorizontalPadding = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextHorizontalPadding, textHorizontalPadding);
|
||||
textMinWidth = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextMinWidth, textMinWidth);
|
||||
customTabLayoutId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_customTabTextLayoutId, customTabLayoutId);
|
||||
customTabTextViewId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_customTabTextViewId, customTabTextViewId);
|
||||
distributeEvenly = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_distributeEvenly, distributeEvenly);
|
||||
clickable = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_clickable, clickable);
|
||||
titleOffset = a.getLayoutDimension(
|
||||
R.styleable.stl_SmartTabLayout_stl_titleOffset, titleOffset);
|
||||
textStyle = a.getResourceId(R.styleable.stl_SmartTabLayout_stl_tabTextStyle, textStyle);
|
||||
a.recycle();
|
||||
|
||||
this.titleOffset = titleOffset;
|
||||
this.tabViewBackgroundResId = tabBackgroundResId;
|
||||
this.tabViewTextAllCaps = textAllCaps;
|
||||
this.tabViewTextColors = (textColors != null)
|
||||
? textColors
|
||||
: ColorStateList.valueOf(TAB_VIEW_TEXT_COLOR);
|
||||
this.tabViewTextSize = textSize;
|
||||
this.tabViewTextHorizontalPadding = textHorizontalPadding;
|
||||
this.tabViewTextMinWidth = textMinWidth;
|
||||
this.internalTabClickListener = clickable ? new InternalTabClickListener() : null;
|
||||
this.distributeEvenly = distributeEvenly;
|
||||
this.textAppearance = textStyle;
|
||||
|
||||
if (customTabLayoutId != NO_ID) {
|
||||
setCustomTabView(customTabLayoutId, customTabTextViewId);
|
||||
}
|
||||
|
||||
this.tabStrip = new SmartTabStrip(context, attrs);
|
||||
|
||||
if (distributeEvenly && tabStrip.isIndicatorAlwaysInCenter()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"'distributeEvenly' and 'indicatorAlwaysInCenter' both use does not support");
|
||||
}
|
||||
|
||||
// Make sure that the Tab Strips fills this View
|
||||
setFillViewport(!tabStrip.isIndicatorAlwaysInCenter());
|
||||
|
||||
addView(tabStrip, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
|
||||
super.onScrollChanged(l, t, oldl, oldt);
|
||||
if (onScrollChangeListener != null) {
|
||||
onScrollChangeListener.onScrollChanged(l, oldl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
if (tabStrip.isIndicatorAlwaysInCenter() && tabStrip.getChildCount() > 0) {
|
||||
View firstTab = tabStrip.getChildAt(0);
|
||||
View lastTab = tabStrip.getChildAt(tabStrip.getChildCount() - 1);
|
||||
int start = (w - Utils.getMeasuredWidth(firstTab)) / 2 - Utils.getMarginStart(firstTab);
|
||||
int end = (w - Utils.getMeasuredWidth(lastTab)) / 2 - Utils.getMarginEnd(lastTab);
|
||||
tabStrip.setMinimumWidth(tabStrip.getMeasuredWidth());
|
||||
ViewCompat.setPaddingRelative(this, start, getPaddingTop(), end, getPaddingBottom());
|
||||
setClipToPadding(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
// Ensure first scroll
|
||||
if (changed && viewPager != null) {
|
||||
scrollToTab(viewPager.getCurrentItem(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the behavior of the Indicator scrolling feedback.
|
||||
*
|
||||
* @param interpolator {@link com.ogaclejapan.smarttablayout.SmartTabIndicationInterpolator}
|
||||
*/
|
||||
public void setIndicationInterpolator(SmartTabIndicationInterpolator interpolator) {
|
||||
tabStrip.setIndicationInterpolator(interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom {@link TabColorizer} to be used.
|
||||
*
|
||||
* If you only require simple customisation then you can use
|
||||
* {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
|
||||
* similar effects.
|
||||
*/
|
||||
public void setCustomTabColorizer(TabColorizer tabColorizer) {
|
||||
tabStrip.setCustomTabColorizer(tabColorizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color used for styling the tab text. This will need to be called prior to calling
|
||||
* {@link #setViewPager(ViewPager)} otherwise it will not get set
|
||||
*
|
||||
* @param color to use for tab text
|
||||
*/
|
||||
public void setDefaultTabTextColor(int color) {
|
||||
tabViewTextColors = ColorStateList.valueOf(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors used for styling the tab text. This will need to be called prior to calling
|
||||
* {@link #setViewPager(ViewPager)} otherwise it will not get set
|
||||
*
|
||||
* @param colors ColorStateList to use for tab text
|
||||
*/
|
||||
public void setDefaultTabTextColor(ColorStateList colors) {
|
||||
tabViewTextColors = colors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the same weight for tab
|
||||
*/
|
||||
public void setDistributeEvenly(boolean distributeEvenly) {
|
||||
this.distributeEvenly = distributeEvenly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to be used for indicating the selected tab. These colors are treated as a
|
||||
* circular array. Providing one color will mean that all tabs are indicated with the same color.
|
||||
*/
|
||||
public void setSelectedIndicatorColors(int... colors) {
|
||||
tabStrip.setSelectedIndicatorColors(colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to be used for tab dividers. These colors are treated as a circular array.
|
||||
* Providing one color will mean that all tabs are indicated with the same color.
|
||||
*/
|
||||
public void setDividerColors(int... colors) {
|
||||
tabStrip.setDividerColors(colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ViewPager.OnPageChangeListener}. When using {@link SmartTabLayout} you are
|
||||
* required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
|
||||
* that the layout can update it's scroll position correctly.
|
||||
*
|
||||
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
|
||||
*/
|
||||
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
|
||||
viewPagerPageChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link OnScrollChangeListener} for obtaining values of scrolling.
|
||||
*
|
||||
* @param listener the {@link OnScrollChangeListener} to set
|
||||
*/
|
||||
public void setOnScrollChangeListener(OnScrollChangeListener listener) {
|
||||
onScrollChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link OnTabClickListener} for obtaining click event.
|
||||
*
|
||||
* @param listener the {@link OnTabClickListener} to set
|
||||
*/
|
||||
public void setOnTabClickListener(OnTabClickListener listener) {
|
||||
onTabClickListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom layout to be inflated for the tab views.
|
||||
*
|
||||
* @param layoutResId Layout id to be inflated
|
||||
* @param textViewId id of the {@link TextView} in the inflated view
|
||||
*/
|
||||
public void setCustomTabView(int layoutResId, int textViewId) {
|
||||
tabProvider = new SimpleTabProvider(getContext(), layoutResId, textViewId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom layout to be inflated for the tab views.
|
||||
*
|
||||
* @param provider {@link TabProvider}
|
||||
*/
|
||||
public void setCustomTabView(TabProvider provider) {
|
||||
tabProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the associated view pager. Note that the assumption here is that the pager content
|
||||
* (number of tabs and tab titles) does not change after this call has been made.
|
||||
*/
|
||||
public void setViewPager(ViewPager viewPager) {
|
||||
tabStrip.removeAllViews();
|
||||
|
||||
this.viewPager = viewPager;
|
||||
if (viewPager != null && viewPager.getAdapter() != null) {
|
||||
viewPager.addOnPageChangeListener(new InternalViewPagerListener());
|
||||
populateTabStrip();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view at the specified position in the tabs.
|
||||
*
|
||||
* @param position the position at which to get the view from
|
||||
* @return the view at the specified position or null if the position does not exist within the
|
||||
* tabs
|
||||
*/
|
||||
public View getTabAt(int position) {
|
||||
return tabStrip.getChildAt(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default view to be used for tabs. This is called if a custom tab view is not set via
|
||||
* {@link #setCustomTabView(int, int)}.
|
||||
*/
|
||||
protected TextView createDefaultTabView(CharSequence title) {
|
||||
TextView textView = new TextView(getContext());
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setText(title);
|
||||
if (textAppearance != NO_TEXT_STYLE) {
|
||||
textView.setTextAppearance(getContext(), textAppearance);
|
||||
} else {
|
||||
textView.setTextColor(tabViewTextColors);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabViewTextSize);
|
||||
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
// If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
|
||||
textView.setAllCaps(tabViewTextAllCaps);
|
||||
}
|
||||
|
||||
if (tabViewTextMinWidth > 0) {
|
||||
textView.setMinWidth(tabViewTextMinWidth);
|
||||
}
|
||||
}
|
||||
if (tabViewBackgroundResId != NO_ID) {
|
||||
textView.setBackgroundResource(tabViewBackgroundResId);
|
||||
} else {
|
||||
// If we're running on Honeycomb or newer, then we can use the Theme's
|
||||
// selectableItemBackground to ensure that the View has a pressed state
|
||||
TypedValue outValue = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
|
||||
outValue, true);
|
||||
textView.setBackgroundResource(outValue.resourceId);
|
||||
}
|
||||
textView.setPadding(
|
||||
tabViewTextHorizontalPadding, 0,
|
||||
tabViewTextHorizontalPadding, 0);
|
||||
textView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
private void populateTabStrip() {
|
||||
final PagerAdapter adapter = viewPager.getAdapter();
|
||||
|
||||
for (int i = 0; i < adapter.getCount(); i++) {
|
||||
|
||||
final View tabView = (tabProvider == null)
|
||||
? createDefaultTabView(adapter.getPageTitle(i))
|
||||
: tabProvider.createTabView(tabStrip, i, adapter);
|
||||
|
||||
if (tabView == null) {
|
||||
throw new IllegalStateException("tabView is null.");
|
||||
}
|
||||
|
||||
if (distributeEvenly) {
|
||||
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();
|
||||
lp.width = 0;
|
||||
lp.weight = 1;
|
||||
}
|
||||
|
||||
if (internalTabClickListener != null) {
|
||||
tabView.setOnClickListener(internalTabClickListener);
|
||||
}
|
||||
|
||||
tabStrip.addView(tabView);
|
||||
|
||||
if (i == viewPager.getCurrentItem()) {
|
||||
tabView.setSelected(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollToTab(int tabIndex, float positionOffset) {
|
||||
final int tabStripChildCount = tabStrip.getChildCount();
|
||||
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isLayoutRtl = Utils.isLayoutRtl(this);
|
||||
View selectedTab = tabStrip.getChildAt(tabIndex);
|
||||
int widthPlusMargin = Utils.getWidth(selectedTab) + Utils.getMarginHorizontally(selectedTab);
|
||||
int extraOffset = (int) (positionOffset * widthPlusMargin);
|
||||
|
||||
if (tabStrip.isIndicatorAlwaysInCenter()) {
|
||||
|
||||
if (0f < positionOffset && positionOffset < 1f) {
|
||||
View nextTab = tabStrip.getChildAt(tabIndex + 1);
|
||||
int selectHalfWidth = Utils.getWidth(selectedTab) / 2 + Utils.getMarginEnd(selectedTab);
|
||||
int nextHalfWidth = Utils.getWidth(nextTab) / 2 + Utils.getMarginStart(nextTab);
|
||||
extraOffset = Math.round(positionOffset * (selectHalfWidth + nextHalfWidth));
|
||||
}
|
||||
|
||||
View firstTab = tabStrip.getChildAt(0);
|
||||
int x;
|
||||
if (isLayoutRtl) {
|
||||
int first = Utils.getWidth(firstTab) + Utils.getMarginEnd(firstTab);
|
||||
int selected = Utils.getWidth(selectedTab) + Utils.getMarginEnd(selectedTab);
|
||||
x = Utils.getEnd(selectedTab) - Utils.getMarginEnd(selectedTab) - extraOffset;
|
||||
x -= (first - selected) / 2;
|
||||
} else {
|
||||
int first = Utils.getWidth(firstTab) + Utils.getMarginStart(firstTab);
|
||||
int selected = Utils.getWidth(selectedTab) + Utils.getMarginStart(selectedTab);
|
||||
x = Utils.getStart(selectedTab) - Utils.getMarginStart(selectedTab) + extraOffset;
|
||||
x -= (first - selected) / 2;
|
||||
}
|
||||
|
||||
scrollTo(x, 0);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
int x;
|
||||
if (titleOffset == TITLE_OFFSET_AUTO_CENTER) {
|
||||
|
||||
if (0f < positionOffset && positionOffset < 1f) {
|
||||
View nextTab = tabStrip.getChildAt(tabIndex + 1);
|
||||
int selectHalfWidth = Utils.getWidth(selectedTab) / 2 + Utils.getMarginEnd(selectedTab);
|
||||
int nextHalfWidth = Utils.getWidth(nextTab) / 2 + Utils.getMarginStart(nextTab);
|
||||
extraOffset = Math.round(positionOffset * (selectHalfWidth + nextHalfWidth));
|
||||
}
|
||||
|
||||
if (isLayoutRtl) {
|
||||
x = -Utils.getWidthWithMargin(selectedTab) / 2 + getWidth() / 2;
|
||||
x -= Utils.getPaddingStart(this);
|
||||
} else {
|
||||
x = Utils.getWidthWithMargin(selectedTab) / 2 - getWidth() / 2;
|
||||
x += Utils.getPaddingStart(this);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (isLayoutRtl) {
|
||||
x = (tabIndex > 0 || positionOffset > 0) ? titleOffset : 0;
|
||||
} else {
|
||||
x = (tabIndex > 0 || positionOffset > 0) ? -titleOffset : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int start = Utils.getStart(selectedTab);
|
||||
int startMargin = Utils.getMarginStart(selectedTab);
|
||||
if (isLayoutRtl) {
|
||||
x += start + startMargin - extraOffset - getWidth() + Utils.getPaddingHorizontally(this);
|
||||
} else {
|
||||
x += start - startMargin + extraOffset;
|
||||
}
|
||||
|
||||
scrollTo(x, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the scroll position of a view changes.
|
||||
*/
|
||||
public interface OnScrollChangeListener {
|
||||
|
||||
/**
|
||||
* Called when the scroll position of a view changes.
|
||||
*
|
||||
* @param scrollX Current horizontal scroll origin.
|
||||
* @param oldScrollX Previous horizontal scroll origin.
|
||||
*/
|
||||
void onScrollChanged(int scrollX, int oldScrollX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a tab is clicked.
|
||||
*/
|
||||
public interface OnTabClickListener {
|
||||
|
||||
/**
|
||||
* Called when a tab is clicked.
|
||||
*
|
||||
* @param position tab's position
|
||||
*/
|
||||
void onTabClicked(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the custom tabs in the tab layout. Set with
|
||||
* {@link #setCustomTabView(com.ogaclejapan.smarttablayout.SmartTabLayout.TabProvider)}
|
||||
*/
|
||||
public interface TabProvider {
|
||||
|
||||
/**
|
||||
* @return Return the View of {@code position} for the Tabs
|
||||
*/
|
||||
View createTabView(ViewGroup container, int position, PagerAdapter adapter);
|
||||
|
||||
}
|
||||
|
||||
private static class SimpleTabProvider implements TabProvider {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final int tabViewLayoutId;
|
||||
private final int tabViewTextViewId;
|
||||
|
||||
private SimpleTabProvider(Context context, int layoutResId, int textViewId) {
|
||||
inflater = LayoutInflater.from(context);
|
||||
tabViewLayoutId = layoutResId;
|
||||
tabViewTextViewId = textViewId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createTabView(ViewGroup container, int position, PagerAdapter adapter) {
|
||||
View tabView = null;
|
||||
TextView tabTitleView = null;
|
||||
|
||||
if (tabViewLayoutId != NO_ID) {
|
||||
tabView = inflater.inflate(tabViewLayoutId, container, false);
|
||||
}
|
||||
|
||||
if (tabViewTextViewId != NO_ID && tabView != null) {
|
||||
tabTitleView = (TextView) tabView.findViewById(tabViewTextViewId);
|
||||
}
|
||||
|
||||
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
|
||||
tabTitleView = (TextView) tabView;
|
||||
}
|
||||
|
||||
if (tabTitleView != null) {
|
||||
tabTitleView.setText(adapter.getPageTitle(position));
|
||||
}
|
||||
|
||||
return tabView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
|
||||
|
||||
private int scrollState;
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
int tabStripChildCount = tabStrip.getChildCount();
|
||||
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tabStrip.onViewPagerPageChanged(position, positionOffset);
|
||||
|
||||
scrollToTab(position, positionOffset);
|
||||
|
||||
if (viewPagerPageChangeListener != null) {
|
||||
viewPagerPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
scrollState = state;
|
||||
|
||||
if (viewPagerPageChangeListener != null) {
|
||||
viewPagerPageChangeListener.onPageScrollStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (scrollState == ViewPager.SCROLL_STATE_IDLE) {
|
||||
tabStrip.onViewPagerPageChanged(position, 0f);
|
||||
scrollToTab(position, 0);
|
||||
}
|
||||
|
||||
for (int i = 0, size = tabStrip.getChildCount(); i < size; i++) {
|
||||
tabStrip.getChildAt(i).setSelected(position == i);
|
||||
}
|
||||
|
||||
if (viewPagerPageChangeListener != null) {
|
||||
viewPagerPageChangeListener.onPageSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class InternalTabClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
for (int i = 0; i < tabStrip.getChildCount(); i++) {
|
||||
if (v == tabStrip.getChildAt(i)) {
|
||||
if (onTabClickListener != null) {
|
||||
onTabClickListener.onTabClicked(i);
|
||||
}
|
||||
viewPager.setCurrentItem(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,705 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.chwl.library.R;
|
||||
|
||||
/**
|
||||
* To be used with ViewPager2 to provide a tab indicator component which give constant feedback as
|
||||
* to
|
||||
* the user's scroll progress.
|
||||
* <p>
|
||||
* To use the component, simply add it to your view hierarchy. Then in your
|
||||
* {@link android.app.Activity} or {@link androidx.fragment.app.Fragment} call
|
||||
* {@link #setViewPager(ViewPager2, RecyclerView.Adapter, TabTitleProvider)} providing it the ViewPager this
|
||||
* layout
|
||||
* is being used for.
|
||||
* <p>
|
||||
* The colors can be customized in two ways. The first and simplest is to provide an array of
|
||||
* colors
|
||||
* via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
|
||||
* alternative is via the {@link TabColorizer} interface which provides you complete control over
|
||||
* which color is used for any individual position.
|
||||
* <p>
|
||||
* The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
|
||||
* providing the layout ID of your custom layout.
|
||||
* <p>
|
||||
* Forked from Google Samples > SlidingTabsBasic >
|
||||
* <a href="https://developer.android.com/samples/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.html">SlidingTabLayout</a>
|
||||
*/
|
||||
public class SmartTabLayout2 extends HorizontalScrollView {
|
||||
|
||||
private static final boolean DEFAULT_DISTRIBUTE_EVENLY = false;
|
||||
private static final int TITLE_OFFSET_DIPS = 24;
|
||||
private static final int TITLE_OFFSET_AUTO_CENTER = -1;
|
||||
private static final int TAB_VIEW_PADDING_DIPS = 16;
|
||||
private static final boolean TAB_VIEW_TEXT_ALL_CAPS = true;
|
||||
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
|
||||
private static final int TAB_VIEW_TEXT_COLOR = 0xFC000000;
|
||||
private static final int TAB_VIEW_TEXT_MIN_WIDTH = 0;
|
||||
private static final boolean TAB_CLICKABLE = true;
|
||||
private static final int NO_TEXT_STYLE = -1;
|
||||
|
||||
protected final SmartTabStrip tabStrip;
|
||||
private int titleOffset;
|
||||
private int tabViewBackgroundResId;
|
||||
private boolean tabViewTextAllCaps;
|
||||
private ColorStateList tabViewTextColors;
|
||||
private float tabViewTextSize;
|
||||
private int tabViewTextHorizontalPadding;
|
||||
private int tabViewTextMinWidth;
|
||||
private ViewPager2 viewPager;
|
||||
private ViewPager2.OnPageChangeCallback viewPagerPageChangeCallback;
|
||||
@Nullable
|
||||
private TabTitleProvider tabTitleProvider;
|
||||
private OnScrollChangeListener onScrollChangeListener;
|
||||
private TabProvider tabProvider;
|
||||
private InternalTabClickListener internalTabClickListener;
|
||||
private InternalTabLongClickListener internalTabLongClickListener;
|
||||
private OnTabClickListener onTabClickListener;
|
||||
private OnTabLongClickListener onTabLongClickListener;
|
||||
private boolean distributeEvenly;
|
||||
private int textAppearance;
|
||||
|
||||
private int childCountCache = 0;
|
||||
|
||||
public SmartTabLayout2(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SmartTabLayout2(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SmartTabLayout2(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
// Disable the Scroll Bar
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
|
||||
final DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
final float density = dm.density;
|
||||
|
||||
int tabBackgroundResId = NO_ID;
|
||||
boolean textAllCaps = TAB_VIEW_TEXT_ALL_CAPS;
|
||||
ColorStateList textColors;
|
||||
float textSize = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP, dm);
|
||||
int textHorizontalPadding = (int) (TAB_VIEW_PADDING_DIPS * density);
|
||||
int textMinWidth = (int) (TAB_VIEW_TEXT_MIN_WIDTH * density);
|
||||
boolean distributeEvenly = DEFAULT_DISTRIBUTE_EVENLY;
|
||||
int customTabLayoutId = NO_ID;
|
||||
int customTabTextViewId = NO_ID;
|
||||
boolean clickable = TAB_CLICKABLE;
|
||||
int titleOffset = (int) (TITLE_OFFSET_DIPS * density);
|
||||
int textStyle = NO_TEXT_STYLE;
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.stl_SmartTabLayout, defStyle, 0);
|
||||
tabBackgroundResId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabBackground, tabBackgroundResId);
|
||||
textAllCaps = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextAllCaps, textAllCaps);
|
||||
textColors = a.getColorStateList(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextColor);
|
||||
textSize = a.getDimension(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextSize, textSize);
|
||||
textHorizontalPadding = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextHorizontalPadding, textHorizontalPadding);
|
||||
textMinWidth = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_defaultTabTextMinWidth, textMinWidth);
|
||||
customTabLayoutId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_customTabTextLayoutId, customTabLayoutId);
|
||||
customTabTextViewId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_customTabTextViewId, customTabTextViewId);
|
||||
distributeEvenly = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_distributeEvenly, distributeEvenly);
|
||||
clickable = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_clickable, clickable);
|
||||
titleOffset = a.getLayoutDimension(
|
||||
R.styleable.stl_SmartTabLayout_stl_titleOffset, titleOffset);
|
||||
textStyle = a.getResourceId(R.styleable.stl_SmartTabLayout_stl_tabTextStyle, textStyle);
|
||||
a.recycle();
|
||||
|
||||
this.titleOffset = titleOffset;
|
||||
this.tabViewBackgroundResId = tabBackgroundResId;
|
||||
this.tabViewTextAllCaps = textAllCaps;
|
||||
this.tabViewTextColors = (textColors != null)
|
||||
? textColors
|
||||
: ColorStateList.valueOf(TAB_VIEW_TEXT_COLOR);
|
||||
this.tabViewTextSize = textSize;
|
||||
this.tabViewTextHorizontalPadding = textHorizontalPadding;
|
||||
this.tabViewTextMinWidth = textMinWidth;
|
||||
this.internalTabClickListener = clickable ? new InternalTabClickListener() : null;
|
||||
this.internalTabLongClickListener = clickable ? new InternalTabLongClickListener() : null;
|
||||
this.distributeEvenly = distributeEvenly;
|
||||
this.textAppearance = textStyle;
|
||||
|
||||
if (customTabLayoutId != NO_ID) {
|
||||
setCustomTabView(customTabLayoutId, customTabTextViewId);
|
||||
}
|
||||
|
||||
this.tabStrip = new SmartTabStrip(context, attrs);
|
||||
|
||||
if (distributeEvenly && tabStrip.isIndicatorAlwaysInCenter()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"'distributeEvenly' and 'indicatorAlwaysInCenter' both use does not support");
|
||||
}
|
||||
|
||||
// Make sure that the Tab Strips fills this View
|
||||
setFillViewport(!tabStrip.isIndicatorAlwaysInCenter());
|
||||
|
||||
addView(tabStrip, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
|
||||
super.onScrollChanged(l, t, oldl, oldt);
|
||||
if (onScrollChangeListener != null) {
|
||||
onScrollChangeListener.onScrollChanged(l, oldl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
adjustForIndicatorAlwaysInCenter(w, h);
|
||||
}
|
||||
|
||||
private void adjustForIndicatorAlwaysInCenter(int w, int h) {
|
||||
if (tabStrip.isIndicatorAlwaysInCenter() && tabStrip.getChildCount() > 0) {
|
||||
View firstTab = tabStrip.getChildAt(0);
|
||||
View lastTab = tabStrip.getChildAt(tabStrip.getChildCount() - 1);
|
||||
int start = (w - Utils.getMeasuredWidth(firstTab)) / 2 - Utils.getMarginStart(firstTab);
|
||||
int end = (w - Utils.getMeasuredWidth(lastTab)) / 2 - Utils.getMarginEnd(lastTab);
|
||||
tabStrip.setMinimumWidth(tabStrip.getMeasuredWidth());
|
||||
ViewCompat.setPaddingRelative(this, start, getPaddingTop(), end, getPaddingBottom());
|
||||
setClipToPadding(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
// Ensure first scroll
|
||||
if (changed && viewPager != null) {
|
||||
scrollToTab(viewPager.getCurrentItem(), 0);
|
||||
}
|
||||
if (childCountCache != tabStrip.getChildCount()) {
|
||||
adjustForIndicatorAlwaysInCenter(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
childCountCache = tabStrip.getChildCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the behavior of the Indicator scrolling feedback.
|
||||
*
|
||||
* @param interpolator {@link SmartTabIndicationInterpolator}
|
||||
*/
|
||||
public void setIndicationInterpolator(SmartTabIndicationInterpolator interpolator) {
|
||||
tabStrip.setIndicationInterpolator(interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom {@link TabColorizer} to be used.
|
||||
*
|
||||
* If you only require simple customisation then you can use
|
||||
* {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
|
||||
* similar effects.
|
||||
*/
|
||||
public void setCustomTabColorizer(TabColorizer tabColorizer) {
|
||||
tabStrip.setCustomTabColorizer(tabColorizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color used for styling the tab text. This will need to be called prior to calling
|
||||
* {@link #setViewPager(ViewPager2, RecyclerView.Adapter, TabTitleProvider)} otherwise it will not get set
|
||||
*
|
||||
* @param color to use for tab text
|
||||
*/
|
||||
public void setDefaultTabTextColor(int color) {
|
||||
tabViewTextColors = ColorStateList.valueOf(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors used for styling the tab text. This will need to be called prior to calling
|
||||
* {@link #setViewPager(ViewPager2, RecyclerView.Adapter, TabTitleProvider)} otherwise it will not get set
|
||||
*
|
||||
* @param colors ColorStateList to use for tab text
|
||||
*/
|
||||
public void setDefaultTabTextColor(ColorStateList colors) {
|
||||
tabViewTextColors = colors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the same weight for tab
|
||||
*/
|
||||
public void setDistributeEvenly(boolean distributeEvenly) {
|
||||
this.distributeEvenly = distributeEvenly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to be used for indicating the selected tab. These colors are treated as a
|
||||
* circular array. Providing one color will mean that all tabs are indicated with the same color.
|
||||
*/
|
||||
public void setSelectedIndicatorColors(int... colors) {
|
||||
tabStrip.setSelectedIndicatorColors(colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to be used for tab dividers. These colors are treated as a circular array.
|
||||
* Providing one color will mean that all tabs are indicated with the same color.
|
||||
*/
|
||||
public void setDividerColors(int... colors) {
|
||||
tabStrip.setDividerColors(colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ViewPager2.OnPageChangeCallback}. When using {@link SmartTabLayout2} you are
|
||||
* required to set any {@link ViewPager2.OnPageChangeCallback} through this method. This is so
|
||||
* that the layout can update it's scroll position correctly.
|
||||
*
|
||||
* @see ViewPager2#registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback)
|
||||
*/
|
||||
public void setOnPageChangeCallback(ViewPager2.OnPageChangeCallback listener) {
|
||||
viewPagerPageChangeCallback = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link OnScrollChangeListener} for obtaining values of scrolling.
|
||||
*
|
||||
* @param listener the {@link OnScrollChangeListener} to set
|
||||
*/
|
||||
public void setOnScrollChangeListener(OnScrollChangeListener listener) {
|
||||
onScrollChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link OnTabClickListener} for obtaining click event.
|
||||
*
|
||||
* @param listener the {@link OnTabClickListener} to set
|
||||
*/
|
||||
public void setOnTabClickListener(OnTabClickListener listener) {
|
||||
onTabClickListener = listener;
|
||||
}
|
||||
|
||||
public void setOnTabLongClickListener(OnTabLongClickListener listener) {
|
||||
onTabLongClickListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom layout to be inflated for the tab views.
|
||||
*
|
||||
* @param layoutResId Layout id to be inflated
|
||||
* @param textViewId id of the {@link TextView} in the inflated view
|
||||
*/
|
||||
public void setCustomTabView(int layoutResId, int textViewId) {
|
||||
tabProvider = new SimpleTabProvider(getContext(), layoutResId, textViewId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom layout to be inflated for the tab views.
|
||||
*
|
||||
* @param provider {@link TabProvider}
|
||||
*/
|
||||
public void setCustomTabView(TabProvider provider) {
|
||||
tabProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the associated view pager. Note that the assumption here is that the pager content
|
||||
* (number of tabs and tab titles) does not change after this call has been made.
|
||||
*/
|
||||
public void setViewPager(@NonNull ViewPager2 viewPager, @NonNull RecyclerView.Adapter adapter,
|
||||
@Nullable TabTitleProvider titleProvider) {
|
||||
tabStrip.removeAllViews();
|
||||
|
||||
this.viewPager = viewPager;
|
||||
if (this.viewPager.getAdapter() != adapter){
|
||||
this.viewPager.setAdapter(adapter);
|
||||
}
|
||||
this.tabTitleProvider = titleProvider;
|
||||
if (viewPager != null && viewPager.getAdapter() != null) {
|
||||
viewPager.registerOnPageChangeCallback(new InternalViewPagerListener());
|
||||
populateTabStrip();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view at the specified position in the tabs.
|
||||
*
|
||||
* @param position the position at which to get the view from
|
||||
* @return the view at the specified position or null if the position does not exist within the
|
||||
* tabs
|
||||
*/
|
||||
public View getTabAt(int position) {
|
||||
return tabStrip.getChildAt(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default view to be used for tabs. This is called if a custom tab view is not set via
|
||||
* {@link #setCustomTabView(int, int)}.
|
||||
*/
|
||||
protected TextView createDefaultTabView(CharSequence title) {
|
||||
TextView textView = new TextView(getContext());
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setText(title);
|
||||
if (textAppearance != NO_TEXT_STYLE) {
|
||||
textView.setTextAppearance(getContext(), textAppearance);
|
||||
} else {
|
||||
textView.setTextColor(tabViewTextColors);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabViewTextSize);
|
||||
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
// If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
|
||||
textView.setAllCaps(tabViewTextAllCaps);
|
||||
}
|
||||
|
||||
if (tabViewTextMinWidth > 0) {
|
||||
textView.setMinWidth(tabViewTextMinWidth);
|
||||
}
|
||||
}
|
||||
if (tabViewBackgroundResId != NO_ID) {
|
||||
textView.setBackgroundResource(tabViewBackgroundResId);
|
||||
} else {
|
||||
// If we're running on Honeycomb or newer, then we can use the Theme's
|
||||
// selectableItemBackground to ensure that the View has a pressed state
|
||||
TypedValue outValue = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
|
||||
outValue, true);
|
||||
textView.setBackgroundResource(outValue.resourceId);
|
||||
}
|
||||
textView.setPadding(
|
||||
tabViewTextHorizontalPadding, 0,
|
||||
tabViewTextHorizontalPadding, 0);
|
||||
textView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
private void populateTabStrip() {
|
||||
final RecyclerView.Adapter adapter = viewPager.getAdapter();
|
||||
|
||||
for (int i = 0; i < adapter.getItemCount(); i++) {
|
||||
|
||||
CharSequence pageTitle = null;
|
||||
if (tabTitleProvider != null) {
|
||||
pageTitle = tabTitleProvider.getPageTitle(i);
|
||||
}
|
||||
|
||||
final View tabView = (tabProvider == null)
|
||||
? createDefaultTabView(pageTitle)
|
||||
: tabProvider.createTabView(tabStrip, i, pageTitle);
|
||||
|
||||
if (tabView == null) {
|
||||
throw new IllegalStateException("tabView is null.");
|
||||
}
|
||||
|
||||
if (distributeEvenly) {
|
||||
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();
|
||||
lp.width = 0;
|
||||
lp.weight = 1;
|
||||
}
|
||||
|
||||
if (internalTabClickListener != null) {
|
||||
tabView.setOnClickListener(internalTabClickListener);
|
||||
}
|
||||
if (internalTabLongClickListener != null) {
|
||||
tabView.setOnLongClickListener(internalTabLongClickListener);
|
||||
}
|
||||
|
||||
tabStrip.addView(tabView);
|
||||
|
||||
if (i == viewPager.getCurrentItem()) {
|
||||
tabView.setSelected(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollToTab(int tabIndex, float positionOffset) {
|
||||
final int tabStripChildCount = tabStrip.getChildCount();
|
||||
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isLayoutRtl = Utils.isLayoutRtl(this);
|
||||
View selectedTab = tabStrip.getChildAt(tabIndex);
|
||||
int widthPlusMargin = Utils.getWidth(selectedTab) + Utils.getMarginHorizontally(selectedTab);
|
||||
int extraOffset = (int) (positionOffset * widthPlusMargin);
|
||||
|
||||
if (tabStrip.isIndicatorAlwaysInCenter()) {
|
||||
|
||||
if (0f < positionOffset && positionOffset < 1f) {
|
||||
View nextTab = tabStrip.getChildAt(tabIndex + 1);
|
||||
int selectHalfWidth = Utils.getWidth(selectedTab) / 2 + Utils.getMarginEnd(selectedTab);
|
||||
int nextHalfWidth = Utils.getWidth(nextTab) / 2 + Utils.getMarginStart(nextTab);
|
||||
extraOffset = Math.round(positionOffset * (selectHalfWidth + nextHalfWidth));
|
||||
}
|
||||
|
||||
View firstTab = tabStrip.getChildAt(0);
|
||||
int x;
|
||||
if (isLayoutRtl) {
|
||||
int first = Utils.getWidth(firstTab) + Utils.getMarginEnd(firstTab);
|
||||
int selected = Utils.getWidth(selectedTab) + Utils.getMarginEnd(selectedTab);
|
||||
x = Utils.getEnd(selectedTab) - Utils.getMarginEnd(selectedTab) - extraOffset;
|
||||
x -= (first - selected) / 2;
|
||||
} else {
|
||||
int first = Utils.getWidth(firstTab) + Utils.getMarginStart(firstTab);
|
||||
int selected = Utils.getWidth(selectedTab) + Utils.getMarginStart(selectedTab);
|
||||
x = Utils.getStart(selectedTab) - Utils.getMarginStart(selectedTab) + extraOffset;
|
||||
x -= (first - selected) / 2;
|
||||
}
|
||||
|
||||
scrollTo(x, 0);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
int x;
|
||||
if (titleOffset == TITLE_OFFSET_AUTO_CENTER) {
|
||||
|
||||
if (0f < positionOffset && positionOffset < 1f) {
|
||||
View nextTab = tabStrip.getChildAt(tabIndex + 1);
|
||||
int selectHalfWidth = Utils.getWidth(selectedTab) / 2 + Utils.getMarginEnd(selectedTab);
|
||||
int nextHalfWidth = Utils.getWidth(nextTab) / 2 + Utils.getMarginStart(nextTab);
|
||||
extraOffset = Math.round(positionOffset * (selectHalfWidth + nextHalfWidth));
|
||||
}
|
||||
|
||||
if (isLayoutRtl) {
|
||||
x = -Utils.getWidthWithMargin(selectedTab) / 2 + getWidth() / 2;
|
||||
x -= Utils.getPaddingStart(this);
|
||||
} else {
|
||||
x = Utils.getWidthWithMargin(selectedTab) / 2 - getWidth() / 2;
|
||||
x += Utils.getPaddingStart(this);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (isLayoutRtl) {
|
||||
x = (tabIndex > 0 || positionOffset > 0) ? titleOffset : 0;
|
||||
} else {
|
||||
x = (tabIndex > 0 || positionOffset > 0) ? -titleOffset : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int start = Utils.getStart(selectedTab);
|
||||
int startMargin = Utils.getMarginStart(selectedTab);
|
||||
if (isLayoutRtl) {
|
||||
x += start + startMargin - extraOffset - getWidth() + Utils.getPaddingHorizontally(this);
|
||||
} else {
|
||||
x += start - startMargin + extraOffset;
|
||||
}
|
||||
|
||||
scrollTo(x, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the scroll position of a view changes.
|
||||
*/
|
||||
public interface OnScrollChangeListener {
|
||||
|
||||
/**
|
||||
* Called when the scroll position of a view changes.
|
||||
*
|
||||
* @param scrollX Current horizontal scroll origin.
|
||||
* @param oldScrollX Previous horizontal scroll origin.
|
||||
*/
|
||||
void onScrollChanged(int scrollX, int oldScrollX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a tab is clicked.
|
||||
*/
|
||||
public interface OnTabClickListener {
|
||||
|
||||
/**
|
||||
* Called when a tab is clicked.
|
||||
*
|
||||
* @param position tab's position
|
||||
*/
|
||||
void onTabClicked(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a tab is long clicked.
|
||||
*/
|
||||
public interface OnTabLongClickListener {
|
||||
|
||||
/**
|
||||
* Called when a tab is clicked.
|
||||
*
|
||||
* @param position tab's position
|
||||
*/
|
||||
void onTabLongClicked(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the custom tabs in the tab layout. Set with
|
||||
* {@link #setCustomTabView(TabProvider)}
|
||||
*/
|
||||
public interface TabProvider {
|
||||
|
||||
/**
|
||||
* @return Return the View of {@code position} for the Tabs
|
||||
*/
|
||||
View createTabView(ViewGroup container, int position, @Nullable CharSequence pageTitle);
|
||||
|
||||
}
|
||||
|
||||
private static class SimpleTabProvider implements TabProvider {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
private final int tabViewLayoutId;
|
||||
private final int tabViewTextViewId;
|
||||
|
||||
private SimpleTabProvider(Context context, int layoutResId, int textViewId) {
|
||||
inflater = LayoutInflater.from(context);
|
||||
tabViewLayoutId = layoutResId;
|
||||
tabViewTextViewId = textViewId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createTabView(ViewGroup container, int position, CharSequence pageTitle) {
|
||||
View tabView = null;
|
||||
TextView tabTitleView = null;
|
||||
|
||||
if (tabViewLayoutId != NO_ID) {
|
||||
tabView = inflater.inflate(tabViewLayoutId, container, false);
|
||||
}
|
||||
|
||||
if (tabViewTextViewId != NO_ID && tabView != null) {
|
||||
tabTitleView = (TextView) tabView.findViewById(tabViewTextViewId);
|
||||
}
|
||||
|
||||
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
|
||||
tabTitleView = (TextView) tabView;
|
||||
}
|
||||
|
||||
if (tabTitleView != null) {
|
||||
tabTitleView.setText(pageTitle);
|
||||
}
|
||||
|
||||
return tabView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class InternalViewPagerListener extends ViewPager2.OnPageChangeCallback {
|
||||
|
||||
private int scrollState;
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
int tabStripChildCount = tabStrip.getChildCount();
|
||||
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tabStrip.onViewPagerPageChanged(position, positionOffset);
|
||||
|
||||
scrollToTab(position, positionOffset);
|
||||
|
||||
if (viewPagerPageChangeCallback != null) {
|
||||
viewPagerPageChangeCallback.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
scrollState = state;
|
||||
|
||||
if (viewPagerPageChangeCallback != null) {
|
||||
viewPagerPageChangeCallback.onPageScrollStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (scrollState == ViewPager2.SCROLL_STATE_IDLE) {
|
||||
tabStrip.onViewPagerPageChanged(position, 0f);
|
||||
scrollToTab(position, 0);
|
||||
}
|
||||
|
||||
for (int i = 0, size = tabStrip.getChildCount(); i < size; i++) {
|
||||
tabStrip.getChildAt(i).setSelected(position == i);
|
||||
}
|
||||
|
||||
if (viewPagerPageChangeCallback != null) {
|
||||
viewPagerPageChangeCallback.onPageSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class InternalTabClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
for (int i = 0; i < tabStrip.getChildCount(); i++) {
|
||||
if (v == tabStrip.getChildAt(i)) {
|
||||
if (onTabClickListener != null) {
|
||||
onTabClickListener.onTabClicked(i);
|
||||
}
|
||||
viewPager.setCurrentItem(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalTabLongClickListener implements OnLongClickListener {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
for (int i = 0; i < tabStrip.getChildCount(); i++) {
|
||||
if (v == tabStrip.getChildAt(i)) {
|
||||
if (onTabLongClickListener != null) {
|
||||
onTabLongClickListener.onTabLongClicked(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.chwl.library.R;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Forked from Google Samples > SlidingTabsBasic >
|
||||
* <a href="https://developer.android.com/samples/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.html">SlidingTabStrip</a>
|
||||
*/
|
||||
class SmartTabStrip extends LinearLayout {
|
||||
|
||||
private static final int GRAVITY_BOTTOM = 0;
|
||||
private static final int GRAVITY_TOP = 1;
|
||||
private static final int GRAVITY_CENTER = 2;
|
||||
|
||||
private static final int AUTO_WIDTH = -1;
|
||||
|
||||
private static final int DEFAULT_TOP_BORDER_THICKNESS_DIPS = 0;
|
||||
private static final byte DEFAULT_TOP_BORDER_COLOR_ALPHA = 0x26;
|
||||
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
|
||||
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
|
||||
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
|
||||
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
|
||||
private static final float DEFAULT_INDICATOR_CORNER_RADIUS = 0f;
|
||||
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
|
||||
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
|
||||
private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
|
||||
private static final boolean DEFAULT_INDICATOR_IN_CENTER = false;
|
||||
private static final boolean DEFAULT_INDICATOR_IN_FRONT = false;
|
||||
private static final boolean DEFAULT_INDICATOR_WITHOUT_PADDING = false;
|
||||
private static final int DEFAULT_INDICATOR_GRAVITY = GRAVITY_BOTTOM;
|
||||
private static final boolean DEFAULT_DRAW_DECORATION_AFTER_TAB = false;
|
||||
|
||||
private final int topBorderThickness;
|
||||
private final int topBorderColor;
|
||||
private final int bottomBorderThickness;
|
||||
private final int bottomBorderColor;
|
||||
private final Paint borderPaint;
|
||||
private final RectF indicatorRectF = new RectF();
|
||||
private final boolean indicatorWithoutPadding;
|
||||
private final boolean indicatorAlwaysInCenter;
|
||||
private final boolean indicatorInFront;
|
||||
private final int indicatorThickness;
|
||||
private final int indicatorWidth;
|
||||
private final int indicatorGravity;
|
||||
private final float indicatorCornerRadius;
|
||||
private final Paint indicatorPaint;
|
||||
private final int dividerThickness;
|
||||
private final Paint dividerPaint;
|
||||
private final float dividerHeight;
|
||||
private final SimpleTabColorizer defaultTabColorizer;
|
||||
private final boolean drawDecorationAfterTab;
|
||||
|
||||
private int lastPosition;
|
||||
private int selectedPosition;
|
||||
private float selectionOffset;
|
||||
private SmartTabIndicationInterpolator indicationInterpolator;
|
||||
private TabColorizer customTabColorizer;
|
||||
|
||||
SmartTabStrip(Context context, AttributeSet attrs) {
|
||||
super(context);
|
||||
setWillNotDraw(false);
|
||||
|
||||
final float density = getResources().getDisplayMetrics().density;
|
||||
|
||||
TypedValue outValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
|
||||
final int themeForegroundColor = outValue.data;
|
||||
|
||||
boolean indicatorWithoutPadding = DEFAULT_INDICATOR_WITHOUT_PADDING;
|
||||
boolean indicatorInFront = DEFAULT_INDICATOR_IN_FRONT;
|
||||
boolean indicatorAlwaysInCenter = DEFAULT_INDICATOR_IN_CENTER;
|
||||
int indicationInterpolatorId = SmartTabIndicationInterpolator.ID_SMART;
|
||||
int indicatorGravity = DEFAULT_INDICATOR_GRAVITY;
|
||||
int indicatorColor = DEFAULT_SELECTED_INDICATOR_COLOR;
|
||||
int indicatorColorsId = NO_ID;
|
||||
int indicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
|
||||
int indicatorWidth = AUTO_WIDTH;
|
||||
float indicatorCornerRadius = DEFAULT_INDICATOR_CORNER_RADIUS * density;
|
||||
int overlineColor = setColorAlpha(themeForegroundColor, DEFAULT_TOP_BORDER_COLOR_ALPHA);
|
||||
int overlineThickness = (int) (DEFAULT_TOP_BORDER_THICKNESS_DIPS * density);
|
||||
int underlineColor = setColorAlpha(themeForegroundColor, DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
|
||||
int underlineThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
|
||||
int dividerColor = setColorAlpha(themeForegroundColor, DEFAULT_DIVIDER_COLOR_ALPHA);
|
||||
int dividerColorsId = NO_ID;
|
||||
int dividerThickness = (int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density);
|
||||
boolean drawDecorationAfterTab = DEFAULT_DRAW_DECORATION_AFTER_TAB;
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.stl_SmartTabLayout);
|
||||
indicatorAlwaysInCenter = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorAlwaysInCenter, indicatorAlwaysInCenter);
|
||||
indicatorWithoutPadding = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorWithoutPadding, indicatorWithoutPadding);
|
||||
indicatorInFront = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorInFront, indicatorInFront);
|
||||
indicationInterpolatorId = a.getInt(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorInterpolation, indicationInterpolatorId);
|
||||
indicatorGravity = a.getInt(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorGravity, indicatorGravity);
|
||||
indicatorColor = a.getColor(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorColor, indicatorColor);
|
||||
indicatorColorsId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorColors, indicatorColorsId);
|
||||
indicatorThickness = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorThickness, indicatorThickness);
|
||||
indicatorWidth = a.getLayoutDimension(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorWidth, indicatorWidth);
|
||||
indicatorCornerRadius = a.getDimension(
|
||||
R.styleable.stl_SmartTabLayout_stl_indicatorCornerRadius, indicatorCornerRadius);
|
||||
overlineColor = a.getColor(
|
||||
R.styleable.stl_SmartTabLayout_stl_overlineColor, overlineColor);
|
||||
overlineThickness = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_overlineThickness, overlineThickness);
|
||||
underlineColor = a.getColor(
|
||||
R.styleable.stl_SmartTabLayout_stl_underlineColor, underlineColor);
|
||||
underlineThickness = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_underlineThickness, underlineThickness);
|
||||
dividerColor = a.getColor(
|
||||
R.styleable.stl_SmartTabLayout_stl_dividerColor, dividerColor);
|
||||
dividerColorsId = a.getResourceId(
|
||||
R.styleable.stl_SmartTabLayout_stl_dividerColors, dividerColorsId);
|
||||
dividerThickness = a.getDimensionPixelSize(
|
||||
R.styleable.stl_SmartTabLayout_stl_dividerThickness, dividerThickness);
|
||||
drawDecorationAfterTab = a.getBoolean(
|
||||
R.styleable.stl_SmartTabLayout_stl_drawDecorationAfterTab, drawDecorationAfterTab);
|
||||
a.recycle();
|
||||
|
||||
final int[] indicatorColors = (indicatorColorsId == NO_ID)
|
||||
? new int[] { indicatorColor }
|
||||
: getResources().getIntArray(indicatorColorsId);
|
||||
|
||||
final int[] dividerColors = (dividerColorsId == NO_ID)
|
||||
? new int[] { dividerColor }
|
||||
: getResources().getIntArray(dividerColorsId);
|
||||
|
||||
this.defaultTabColorizer = new SimpleTabColorizer();
|
||||
this.defaultTabColorizer.setIndicatorColors(indicatorColors);
|
||||
this.defaultTabColorizer.setDividerColors(dividerColors);
|
||||
|
||||
this.topBorderThickness = overlineThickness;
|
||||
this.topBorderColor = overlineColor;
|
||||
this.bottomBorderThickness = underlineThickness;
|
||||
this.bottomBorderColor = underlineColor;
|
||||
this.borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
this.indicatorAlwaysInCenter = indicatorAlwaysInCenter;
|
||||
this.indicatorWithoutPadding = indicatorWithoutPadding;
|
||||
this.indicatorInFront = indicatorInFront;
|
||||
this.indicatorThickness = indicatorThickness;
|
||||
this.indicatorWidth = indicatorWidth;
|
||||
this.indicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
this.indicatorCornerRadius = indicatorCornerRadius;
|
||||
this.indicatorGravity = indicatorGravity;
|
||||
|
||||
this.dividerHeight = DEFAULT_DIVIDER_HEIGHT;
|
||||
this.dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
this.dividerPaint.setStrokeWidth(dividerThickness);
|
||||
this.dividerThickness = dividerThickness;
|
||||
|
||||
this.drawDecorationAfterTab = drawDecorationAfterTab;
|
||||
|
||||
this.indicationInterpolator = SmartTabIndicationInterpolator.of(indicationInterpolatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
|
||||
*/
|
||||
private static int setColorAlpha(int color, byte alpha) {
|
||||
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
|
||||
}
|
||||
|
||||
/**
|
||||
* Blend {@code color1} and {@code color2} using the given ratio.
|
||||
*
|
||||
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
|
||||
* 0.0 will return {@code color2}.
|
||||
*/
|
||||
private static int blendColors(int color1, int color2, float ratio) {
|
||||
final float inverseRation = 1f - ratio;
|
||||
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
|
||||
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
|
||||
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
|
||||
return Color.rgb((int) r, (int) g, (int) b);
|
||||
}
|
||||
|
||||
void setIndicationInterpolator(SmartTabIndicationInterpolator interpolator) {
|
||||
indicationInterpolator = interpolator;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void setCustomTabColorizer(TabColorizer customTabColorizer) {
|
||||
this.customTabColorizer = customTabColorizer;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void setSelectedIndicatorColors(int... colors) {
|
||||
// Make sure that the custom colorizer is removed
|
||||
customTabColorizer = null;
|
||||
defaultTabColorizer.setIndicatorColors(colors);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void setDividerColors(int... colors) {
|
||||
// Make sure that the custom colorizer is removed
|
||||
customTabColorizer = null;
|
||||
defaultTabColorizer.setDividerColors(colors);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void onViewPagerPageChanged(int position, float positionOffset) {
|
||||
selectedPosition = position;
|
||||
selectionOffset = positionOffset;
|
||||
if (positionOffset == 0f && lastPosition != selectedPosition) {
|
||||
lastPosition = selectedPosition;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
boolean isIndicatorAlwaysInCenter() {
|
||||
return indicatorAlwaysInCenter;
|
||||
}
|
||||
|
||||
TabColorizer getTabColorizer() {
|
||||
return (customTabColorizer != null) ? customTabColorizer : defaultTabColorizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (!drawDecorationAfterTab) {
|
||||
drawDecoration(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
if (drawDecorationAfterTab) {
|
||||
drawDecoration(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawDecoration(Canvas canvas) {
|
||||
final int height = getHeight();
|
||||
final int width = getWidth();
|
||||
final int tabCount = getChildCount();
|
||||
final TabColorizer tabColorizer = getTabColorizer();
|
||||
final boolean isLayoutRtl = Utils.isLayoutRtl(this);
|
||||
|
||||
if (indicatorInFront) {
|
||||
drawOverline(canvas, 0, width);
|
||||
drawUnderline(canvas, 0, width, height);
|
||||
}
|
||||
|
||||
// Thick colored underline below the current selection
|
||||
if (tabCount > 0) {
|
||||
View selectedTab = getChildAt(selectedPosition);
|
||||
int selectedStart = Utils.getStart(selectedTab, indicatorWithoutPadding);
|
||||
int selectedEnd = Utils.getEnd(selectedTab, indicatorWithoutPadding);
|
||||
int left;
|
||||
int right;
|
||||
if (isLayoutRtl) {
|
||||
left = selectedEnd;
|
||||
right = selectedStart;
|
||||
} else {
|
||||
left = selectedStart;
|
||||
right = selectedEnd;
|
||||
}
|
||||
|
||||
int color = tabColorizer.getIndicatorColor(selectedPosition);
|
||||
float thickness = indicatorThickness;
|
||||
|
||||
if (selectionOffset > 0f && selectedPosition < (getChildCount() - 1)) {
|
||||
int nextColor = tabColorizer.getIndicatorColor(selectedPosition + 1);
|
||||
if (color != nextColor) {
|
||||
color = blendColors(nextColor, color, selectionOffset);
|
||||
}
|
||||
|
||||
// Draw the selection partway between the tabs
|
||||
float startOffset = indicationInterpolator.getLeftEdge(selectionOffset);
|
||||
float endOffset = indicationInterpolator.getRightEdge(selectionOffset);
|
||||
float thicknessOffset = indicationInterpolator.getThickness(selectionOffset);
|
||||
|
||||
View nextTab = getChildAt(selectedPosition + 1);
|
||||
int nextStart = Utils.getStart(nextTab, indicatorWithoutPadding);
|
||||
int nextEnd = Utils.getEnd(nextTab, indicatorWithoutPadding);
|
||||
if (isLayoutRtl) {
|
||||
left = (int) (endOffset * nextEnd + (1.0f - endOffset) * left);
|
||||
right = (int) (startOffset * nextStart + (1.0f - startOffset) * right);
|
||||
} else {
|
||||
left = (int) (startOffset * nextStart + (1.0f - startOffset) * left);
|
||||
right = (int) (endOffset * nextEnd + (1.0f - endOffset) * right);
|
||||
}
|
||||
thickness = thickness * thicknessOffset;
|
||||
}
|
||||
|
||||
drawIndicator(canvas, left, right, height, thickness, color);
|
||||
|
||||
}
|
||||
|
||||
if (!indicatorInFront) {
|
||||
drawOverline(canvas, 0, width);
|
||||
drawUnderline(canvas, 0, getWidth(), height);
|
||||
}
|
||||
|
||||
// Vertical separators between the titles
|
||||
drawSeparator(canvas, height, tabCount);
|
||||
|
||||
}
|
||||
|
||||
private void drawSeparator(Canvas canvas, int height, int tabCount) {
|
||||
if (dividerThickness <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int dividerHeightPx = (int) (Math.min(Math.max(0f, dividerHeight), 1f) * height);
|
||||
final TabColorizer tabColorizer = getTabColorizer();
|
||||
|
||||
// Vertical separators between the titles
|
||||
final int separatorTop = (height - dividerHeightPx) / 2;
|
||||
final int separatorBottom = separatorTop + dividerHeightPx;
|
||||
|
||||
final boolean isLayoutRtl = Utils.isLayoutRtl(this);
|
||||
for (int i = 0; i < tabCount - 1; i++) {
|
||||
View child = getChildAt(i);
|
||||
int end = Utils.getEnd(child);
|
||||
int endMargin = Utils.getMarginEnd(child);
|
||||
int separatorX = isLayoutRtl ? end - endMargin : end + endMargin;
|
||||
dividerPaint.setColor(tabColorizer.getDividerColor(i));
|
||||
canvas.drawLine(separatorX, separatorTop, separatorX, separatorBottom, dividerPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawIndicator(Canvas canvas, int left, int right, int height, float thickness,
|
||||
int color) {
|
||||
if (indicatorThickness <= 0 || indicatorWidth == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
float center;
|
||||
float top;
|
||||
float bottom;
|
||||
|
||||
switch (indicatorGravity) {
|
||||
case GRAVITY_TOP:
|
||||
center = indicatorThickness / 2f;
|
||||
top = center - (thickness / 2f);
|
||||
bottom = center + (thickness / 2f);
|
||||
break;
|
||||
case GRAVITY_CENTER:
|
||||
center = height / 2f;
|
||||
top = center - (thickness / 2f);
|
||||
bottom = center + (thickness / 2f);
|
||||
break;
|
||||
case GRAVITY_BOTTOM:
|
||||
default:
|
||||
center = height - (indicatorThickness / 2f);
|
||||
top = center - (thickness / 2f);
|
||||
bottom = center + (thickness / 2f);
|
||||
}
|
||||
|
||||
indicatorPaint.setColor(color);
|
||||
if (indicatorWidth == AUTO_WIDTH) {
|
||||
indicatorRectF.set(left, top, right, bottom);
|
||||
} else {
|
||||
float padding = (Math.abs(left - right) - indicatorWidth) / 2f;
|
||||
indicatorRectF.set(left + padding, top, right - padding, bottom);
|
||||
}
|
||||
|
||||
if (indicatorCornerRadius > 0f) {
|
||||
canvas.drawRoundRect(
|
||||
indicatorRectF, indicatorCornerRadius,
|
||||
indicatorCornerRadius, indicatorPaint);
|
||||
} else {
|
||||
canvas.drawRect(indicatorRectF, indicatorPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawOverline(Canvas canvas, int left, int right) {
|
||||
if (topBorderThickness <= 0) {
|
||||
return;
|
||||
}
|
||||
// Thin overline along the entire top edge
|
||||
borderPaint.setColor(topBorderColor);
|
||||
canvas.drawRect(left, 0, right, topBorderThickness, borderPaint);
|
||||
}
|
||||
|
||||
private void drawUnderline(Canvas canvas, int left, int right, int height) {
|
||||
if (bottomBorderThickness <= 0) {
|
||||
return;
|
||||
}
|
||||
// Thin underline along the entire bottom edge
|
||||
borderPaint.setColor(bottomBorderColor);
|
||||
canvas.drawRect(left, height - bottomBorderThickness, right, height, borderPaint);
|
||||
}
|
||||
|
||||
private static class SimpleTabColorizer implements TabColorizer {
|
||||
|
||||
private int[] indicatorColors;
|
||||
private int[] dividerColors;
|
||||
|
||||
@Override
|
||||
public final int getIndicatorColor(int position) {
|
||||
return indicatorColors[position % indicatorColors.length];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getDividerColor(int position) {
|
||||
return dividerColors[position % dividerColors.length];
|
||||
}
|
||||
|
||||
void setIndicatorColors(int... colors) {
|
||||
indicatorColors = colors;
|
||||
}
|
||||
|
||||
void setDividerColors(int... colors) {
|
||||
dividerColors = colors;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.chwl.library.widget.tab;
|
||||
|
||||
/**
|
||||
* Allows complete control over the colors drawn in the tab layout. Set with
|
||||
* {@link SmartTabLayout#setCustomTabColorizer(TabColorizer)}.
|
||||
*/
|
||||
public interface TabColorizer {
|
||||
|
||||
/**
|
||||
* @return return the color of the indicator used when {@code position} is selected.
|
||||
*/
|
||||
int getIndicatorColor(int position);
|
||||
|
||||
/**
|
||||
* @return return the color of the divider drawn to the right of {@code position}.
|
||||
*/
|
||||
int getDividerColor(int position);
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package com.chwl.library.widget.tab;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface TabTitleProvider {
|
||||
@Nullable
|
||||
CharSequence getPageTitle(int position);
|
||||
}
|
120
library/src/main/java/com/chwl/library/widget/tab/Utils.java
Normal file
120
library/src/main/java/com/chwl/library/widget/tab/Utils.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.view.MarginLayoutParamsCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
final class Utils {
|
||||
|
||||
private Utils() { }
|
||||
|
||||
static int getMeasuredWidth(View v) {
|
||||
return (v == null) ? 0 : v.getMeasuredWidth();
|
||||
}
|
||||
|
||||
static int getWidth(View v) {
|
||||
return (v == null) ? 0 : v.getWidth();
|
||||
}
|
||||
|
||||
static int getWidthWithMargin(View v) {
|
||||
return getWidth(v) + getMarginHorizontally(v);
|
||||
}
|
||||
|
||||
static int getStart(View v) {
|
||||
return getStart(v, false);
|
||||
}
|
||||
|
||||
static int getStart(View v, boolean withoutPadding) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
if (isLayoutRtl(v)) {
|
||||
return (withoutPadding) ? v.getRight() - getPaddingStart(v) : v.getRight();
|
||||
} else {
|
||||
return (withoutPadding) ? v.getLeft() + getPaddingStart(v) : v.getLeft();
|
||||
}
|
||||
}
|
||||
|
||||
static int getEnd(View v) {
|
||||
return getEnd(v, false);
|
||||
}
|
||||
|
||||
static int getEnd(View v, boolean withoutPadding) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
if (isLayoutRtl(v)) {
|
||||
return (withoutPadding) ? v.getLeft() + getPaddingEnd(v) : v.getLeft();
|
||||
} else {
|
||||
return (withoutPadding) ? v.getRight() - getPaddingEnd(v) : v.getRight();
|
||||
}
|
||||
}
|
||||
|
||||
static int getPaddingStart(View v) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
return ViewCompat.getPaddingStart(v);
|
||||
}
|
||||
|
||||
static int getPaddingEnd(View v) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
return ViewCompat.getPaddingEnd(v);
|
||||
}
|
||||
|
||||
static int getPaddingHorizontally(View v) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
return v.getPaddingLeft() + v.getPaddingRight();
|
||||
}
|
||||
|
||||
static int getMarginStart(View v) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
||||
return MarginLayoutParamsCompat.getMarginStart(lp);
|
||||
}
|
||||
|
||||
static int getMarginEnd(View v) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
||||
return MarginLayoutParamsCompat.getMarginEnd(lp);
|
||||
}
|
||||
|
||||
static int getMarginHorizontally(View v) {
|
||||
if (v == null) {
|
||||
return 0;
|
||||
}
|
||||
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
||||
return MarginLayoutParamsCompat.getMarginStart(lp) + MarginLayoutParamsCompat.getMarginEnd(lp);
|
||||
}
|
||||
|
||||
static boolean isLayoutRtl(View v) {
|
||||
return ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_RTL;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.chwl.library.widget.tab.util
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
|
||||
class FragmentPageAdapter(fragmentManager: FragmentActivity, var fgList: List<Fragment>?) :
|
||||
FragmentStateAdapter(fragmentManager) {
|
||||
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return fgList?.size?:0
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return fgList?.get(position) ?: Fragment()
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util;
|
||||
|
||||
public abstract class PagerItem {
|
||||
|
||||
protected static final float DEFAULT_WIDTH = 1.f;
|
||||
|
||||
private final CharSequence title;
|
||||
private final float width;
|
||||
|
||||
protected PagerItem(CharSequence title, float width) {
|
||||
this.title = title;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class PagerItems<T extends PagerItem> extends ArrayList<T> {
|
||||
|
||||
private final Context context;
|
||||
|
||||
protected PagerItems(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
|
||||
public class ViewPagerItem extends PagerItem {
|
||||
|
||||
private final int resource;
|
||||
|
||||
protected ViewPagerItem(CharSequence title, float width, @LayoutRes int resource) {
|
||||
super(title, width);
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public static ViewPagerItem of(CharSequence title, @LayoutRes int resource) {
|
||||
return of(title, DEFAULT_WIDTH, resource);
|
||||
}
|
||||
|
||||
public static ViewPagerItem of(CharSequence title, float width, @LayoutRes int resource) {
|
||||
return new ViewPagerItem(title, width, resource);
|
||||
}
|
||||
|
||||
public View initiate(LayoutInflater inflater, ViewGroup container) {
|
||||
return inflater.inflate(resource, container, false);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.collection.SparseArrayCompat;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class ViewPagerItemAdapter extends PagerAdapter {
|
||||
|
||||
private final ViewPagerItems pages;
|
||||
private final SparseArrayCompat<WeakReference<View>> holder;
|
||||
private final LayoutInflater inflater;
|
||||
|
||||
public ViewPagerItemAdapter(ViewPagerItems pages) {
|
||||
this.pages = pages;
|
||||
this.holder = new SparseArrayCompat<>(pages.size());
|
||||
this.inflater = LayoutInflater.from(pages.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pages.size();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||
View view = getPagerItem(position).initiate(inflater, container);
|
||||
container.addView(view);
|
||||
holder.put(position, new WeakReference<View>(view));
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
holder.remove(position);
|
||||
container.removeView((View) object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
|
||||
return object == view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return getPagerItem(position).getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPageWidth(int position) {
|
||||
return getPagerItem(position).getWidth();
|
||||
}
|
||||
|
||||
public View getPage(int position) {
|
||||
final WeakReference<View> weakRefItem = holder.get(position);
|
||||
return (weakRefItem != null) ? weakRefItem.get() : null;
|
||||
}
|
||||
|
||||
protected ViewPagerItem getPagerItem(int position) {
|
||||
return pages.get(position);
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
public class ViewPagerItems extends PagerItems<ViewPagerItem> {
|
||||
|
||||
public ViewPagerItems(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public static Creator with(Context context) {
|
||||
return new Creator(context);
|
||||
}
|
||||
|
||||
public static class Creator {
|
||||
|
||||
private final ViewPagerItems items;
|
||||
|
||||
public Creator(Context context) {
|
||||
items = new ViewPagerItems(context);
|
||||
}
|
||||
|
||||
public Creator add(@StringRes int title, @LayoutRes int resource) {
|
||||
return add(ViewPagerItem.of(items.getContext().getString(title), resource));
|
||||
}
|
||||
|
||||
public Creator add(@StringRes int title, float width, @LayoutRes int resource) {
|
||||
return add(ViewPagerItem.of(items.getContext().getString(title), width, resource));
|
||||
}
|
||||
|
||||
public Creator add(CharSequence title, @LayoutRes int resource) {
|
||||
return add(ViewPagerItem.of(title, resource));
|
||||
}
|
||||
|
||||
public Creator add(ViewPagerItem item) {
|
||||
items.add(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewPagerItems create() {
|
||||
return items;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,520 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util.v4;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Size;
|
||||
import android.util.SizeF;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Bundler {
|
||||
|
||||
private final Bundle bundle;
|
||||
|
||||
/**
|
||||
* Constructs a new, empty Bundle.
|
||||
*/
|
||||
public Bundler() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private Bundler(Bundle b) {
|
||||
bundle = (b == null) ? new Bundle() : new Bundle(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Bundle containing a copy of the mappings from the given
|
||||
* Bundle.
|
||||
*
|
||||
* @param b a Bundle to be copied.
|
||||
*/
|
||||
public static Bundler of(Bundle b) {
|
||||
return new Bundler(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts all mappings from the given Bundle into this Bundle.
|
||||
*
|
||||
* @param bundle a Bundle
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putAll(Bundle bundle) {
|
||||
this.bundle.putAll(bundle);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a byte value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a byte
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putByte(String key, byte value) {
|
||||
bundle.putByte(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a char value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a char, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putChar(String key, char value) {
|
||||
bundle.putChar(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a short value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a short
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putShort(String key, short value) {
|
||||
bundle.putShort(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a float value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a float
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putFloat(String key, float value) {
|
||||
bundle.putFloat(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a CharSequence value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a CharSequence, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putCharSequence(String key, CharSequence value) {
|
||||
bundle.putCharSequence(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a Parcelable value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a Parcelable object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putParcelable(String key, Parcelable value) {
|
||||
bundle.putParcelable(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a Size value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a Size object, or null
|
||||
* @return this
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public Bundler putSize(String key, Size value) {
|
||||
bundle.putSize(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a SizeF value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a SizeF object, or null
|
||||
* @return this
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public Bundler putSizeF(String key, SizeF value) {
|
||||
bundle.putSizeF(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an array of Parcelable values into the mapping of this Bundle,
|
||||
* replacing any existing value for the given key. Either key or value may
|
||||
* be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an array of Parcelable objects, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putParcelableArray(String key, Parcelable[] value) {
|
||||
bundle.putParcelableArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a List of Parcelable values into the mapping of this Bundle,
|
||||
* replacing any existing value for the given key. Either key or value may
|
||||
* be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an ArrayList of Parcelable objects, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putParcelableArrayList(String key,
|
||||
ArrayList<? extends Parcelable> value) {
|
||||
bundle.putParcelableArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a SparceArray of Parcelable values into the mapping of this
|
||||
* Bundle, replacing any existing value for the given key. Either key
|
||||
* or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a SparseArray of Parcelable objects, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putSparseParcelableArray(String key,
|
||||
SparseArray<? extends Parcelable> value) {
|
||||
bundle.putSparseParcelableArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an ArrayList<Integer> object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putIntegerArrayList(String key, ArrayList<Integer> value) {
|
||||
bundle.putIntegerArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an ArrayList<String> object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putStringArrayList(String key, ArrayList<String> value) {
|
||||
bundle.putStringArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an ArrayList<CharSequence> object, or null
|
||||
* @return this
|
||||
*/
|
||||
@TargetApi(8)
|
||||
public Bundler putCharSequenceArrayList(String key, ArrayList<CharSequence> value) {
|
||||
bundle.putCharSequenceArrayList(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a Serializable value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a Serializable object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putSerializable(String key, Serializable value) {
|
||||
bundle.putSerializable(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a byte array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a byte array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putByteArray(String key, byte[] value) {
|
||||
bundle.putByteArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a short array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a short array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putShortArray(String key, short[] value) {
|
||||
bundle.putShortArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a char array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a char array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putCharArray(String key, char[] value) {
|
||||
bundle.putCharArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a float array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a float array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putFloatArray(String key, float[] value) {
|
||||
bundle.putFloatArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a CharSequence array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a CharSequence array object, or null
|
||||
* @return this
|
||||
*/
|
||||
@TargetApi(8)
|
||||
public Bundler putCharSequenceArray(String key, CharSequence[] value) {
|
||||
bundle.putCharSequenceArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a Bundle value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a Bundle object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putBundle(String key, Bundle value) {
|
||||
bundle.putBundle(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an {@link IBinder} value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* <p class="note">You should be very careful when using this function. In many
|
||||
* places where Bundles are used (such as inside of Intent objects), the Bundle
|
||||
* can live longer inside of another process than the process that had originally
|
||||
* created it. In that case, the IBinder you supply here will become invalid
|
||||
* when your process goes away, and no longer usable, even if a new process is
|
||||
* created for you later on.</p>
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an IBinder object, or null
|
||||
* @return this
|
||||
*/
|
||||
@TargetApi(18)
|
||||
public Bundler putBinder(String key, IBinder value) {
|
||||
bundle.putBinder(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a Boolean value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a Boolean, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putBoolean(String key, boolean value) {
|
||||
bundle.putBoolean(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an int value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an int, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putInt(String key, int value) {
|
||||
bundle.putInt(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a long value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a long
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putLong(String key, long value) {
|
||||
bundle.putLong(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a double value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a double
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putDouble(String key, double value) {
|
||||
bundle.putDouble(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a String value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a String, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putString(String key, String value) {
|
||||
bundle.putString(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a boolean array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a boolean array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putBooleanArray(String key, boolean[] value) {
|
||||
bundle.putBooleanArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an int array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value an int array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putIntArray(String key, int[] value) {
|
||||
bundle.putIntArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a long array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a long array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putLongArray(String key, long[] value) {
|
||||
bundle.putLongArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a double array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a double array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putDoubleArray(String key, double[] value) {
|
||||
bundle.putDoubleArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a String array value into the mapping of this Bundle, replacing
|
||||
* any existing value for the given key. Either key or value may be null.
|
||||
*
|
||||
* @param key a String, or null
|
||||
* @param value a String array object, or null
|
||||
* @return this
|
||||
*/
|
||||
public Bundler putStringArray(String key, String[] value) {
|
||||
bundle.putStringArray(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bundle.
|
||||
*
|
||||
* @return a bundle
|
||||
*/
|
||||
public Bundle get() {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the argument of Fragment.
|
||||
*
|
||||
* @param fragment a fragment
|
||||
* @return a fragment
|
||||
*/
|
||||
public <T extends Fragment> T into(T fragment) {
|
||||
fragment.setArguments(get());
|
||||
return fragment;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util.v4;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.chwl.library.widget.tab.util.PagerItem;
|
||||
|
||||
|
||||
public class FragmentPagerItem extends PagerItem {
|
||||
|
||||
private static final String TAG = "FragmentPagerItem";
|
||||
private static final String KEY_POSITION = TAG + ":Position";
|
||||
|
||||
private final String className;
|
||||
private final Bundle args;
|
||||
|
||||
protected FragmentPagerItem(CharSequence title, float width, String className, Bundle args) {
|
||||
super(title, width);
|
||||
this.className = className;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public static FragmentPagerItem of(CharSequence title, Class<? extends Fragment> clazz) {
|
||||
return of(title, DEFAULT_WIDTH, clazz);
|
||||
}
|
||||
|
||||
public static FragmentPagerItem of(CharSequence title, Class<? extends Fragment> clazz,
|
||||
Bundle args) {
|
||||
return of(title, DEFAULT_WIDTH, clazz, args);
|
||||
}
|
||||
|
||||
public static FragmentPagerItem of(CharSequence title, float width,
|
||||
Class<? extends Fragment> clazz) {
|
||||
return of(title, width, clazz, new Bundle());
|
||||
}
|
||||
|
||||
public static FragmentPagerItem of(CharSequence title, float width,
|
||||
Class<? extends Fragment> clazz, Bundle args) {
|
||||
return new FragmentPagerItem(title, width, clazz.getName(), args);
|
||||
}
|
||||
|
||||
public static boolean hasPosition(Bundle args) {
|
||||
return args != null && args.containsKey(KEY_POSITION);
|
||||
}
|
||||
|
||||
public static int getPosition(Bundle args) {
|
||||
return (hasPosition(args)) ? args.getInt(KEY_POSITION) : 0;
|
||||
}
|
||||
|
||||
static void setPosition(Bundle args, int position) {
|
||||
args.putInt(KEY_POSITION, position);
|
||||
}
|
||||
|
||||
public Fragment instantiate(Context context, int position) {
|
||||
setPosition(args, position);
|
||||
return Fragment.instantiate(context, className, args);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util.v4;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.collection.SparseArrayCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class FragmentPagerItemAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private final FragmentPagerItems pages;
|
||||
private final SparseArrayCompat<WeakReference<Fragment>> holder;
|
||||
|
||||
public FragmentPagerItemAdapter(FragmentManager fm, FragmentPagerItems pages) {
|
||||
super(fm);
|
||||
this.pages = pages;
|
||||
this.holder = new SparseArrayCompat<>(pages.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return getPagerItem(position).instantiate(pages.getContext(), position);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||
Object item = super.instantiateItem(container, position);
|
||||
if (item instanceof Fragment) {
|
||||
holder.put(position, new WeakReference<Fragment>((Fragment) item));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
holder.remove(position);
|
||||
super.destroyItem(container, position, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return getPagerItem(position).getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPageWidth(int position) {
|
||||
return super.getPageWidth(position);
|
||||
}
|
||||
|
||||
public Fragment getPage(int position) {
|
||||
final WeakReference<Fragment> weakRefItem = holder.get(position);
|
||||
return (weakRefItem != null) ? weakRefItem.get() : null;
|
||||
}
|
||||
|
||||
protected FragmentPagerItem getPagerItem(int position) {
|
||||
return pages.get(position);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util.v4;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.chwl.library.widget.tab.util.PagerItems;
|
||||
|
||||
|
||||
public class FragmentPagerItems extends PagerItems<FragmentPagerItem> {
|
||||
|
||||
public FragmentPagerItems(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public static Creator with(Context context) {
|
||||
return new Creator(context);
|
||||
}
|
||||
|
||||
public static class Creator {
|
||||
|
||||
private final FragmentPagerItems items;
|
||||
|
||||
public Creator(Context context) {
|
||||
items = new FragmentPagerItems(context);
|
||||
}
|
||||
|
||||
public Creator add(@StringRes int title, Class<? extends Fragment> clazz) {
|
||||
return add(FragmentPagerItem.of(items.getContext().getString(title), clazz));
|
||||
}
|
||||
|
||||
public Creator add(@StringRes int title, Class<? extends Fragment> clazz, Bundle args) {
|
||||
return add(FragmentPagerItem.of(items.getContext().getString(title), clazz, args));
|
||||
}
|
||||
|
||||
public Creator add(@StringRes int title, float width, Class<? extends Fragment> clazz) {
|
||||
return add(FragmentPagerItem.of(items.getContext().getString(title), width, clazz));
|
||||
}
|
||||
|
||||
public Creator add(@StringRes int title, float width, Class<? extends Fragment> clazz,
|
||||
Bundle args) {
|
||||
return add(FragmentPagerItem.of(items.getContext().getString(title), width, clazz, args));
|
||||
}
|
||||
|
||||
public Creator add(CharSequence title, Class<? extends Fragment> clazz) {
|
||||
return add(FragmentPagerItem.of(title, clazz));
|
||||
}
|
||||
|
||||
public Creator add(CharSequence title, Class<? extends Fragment> clazz, Bundle args) {
|
||||
return add(FragmentPagerItem.of(title, clazz, args));
|
||||
}
|
||||
|
||||
public Creator add(FragmentPagerItem item) {
|
||||
items.add(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FragmentPagerItems create() {
|
||||
return items;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (C) 2015 ogaclejapan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chwl.library.widget.tab.util.v4;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.collection.SparseArrayCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class FragmentStatePagerItemAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private final FragmentPagerItems pages;
|
||||
private final SparseArrayCompat<WeakReference<Fragment>> holder;
|
||||
|
||||
public FragmentStatePagerItemAdapter(FragmentManager fm, FragmentPagerItems pages) {
|
||||
super(fm);
|
||||
this.pages = pages;
|
||||
this.holder = new SparseArrayCompat<>(pages.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return getPagerItem(position).instantiate(pages.getContext(), position);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||
Object item = super.instantiateItem(container, position);
|
||||
if (item instanceof Fragment) {
|
||||
holder.put(position, new WeakReference<Fragment>((Fragment) item));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||
holder.remove(position);
|
||||
super.destroyItem(container, position, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return getPagerItem(position).getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPageWidth(int position) {
|
||||
return getPagerItem(position).getWidth();
|
||||
}
|
||||
|
||||
public Fragment getPage(int position) {
|
||||
final WeakReference<Fragment> weakRefItem = holder.get(position);
|
||||
return (weakRefItem != null) ? weakRefItem.get() : null;
|
||||
}
|
||||
|
||||
protected FragmentPagerItem getPagerItem(int position) {
|
||||
return pages.get(position);
|
||||
}
|
||||
|
||||
}
|
@@ -120,7 +120,7 @@ public class DrawableTextView extends AppCompatTextView {
|
||||
strikeColor = a.getColor(R.styleable.DrawableTextView_dt_strikeColor, -1);
|
||||
strikeWidth = a.getDimensionPixelSize(R.styleable.DrawableTextView_dt_strikeWidth, 0);
|
||||
//填充颜色
|
||||
soildColor = a.getColor(R.styleable.DrawableTextView_dt_soildColor, 0xffffffff);
|
||||
soildColor = a.getColor(R.styleable.DrawableTextView_dt_soildColor, 0x00000000);
|
||||
//渐变相关
|
||||
angle = a.getInt(R.styleable.DrawableTextView_dt_angle, 0);
|
||||
startColor = a.getColor(R.styleable.DrawableTextView_dt_startColor, -1);
|
||||
|
@@ -191,4 +191,64 @@
|
||||
<attr name="dt_lineColor" format="color" />
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<declare-styleable name="stl_SmartTabLayout">
|
||||
<!-- 设置为true时与权重互斥, 不能都设置为true-->
|
||||
<attr name="stl_indicatorAlwaysInCenter" format="boolean"/>
|
||||
<attr name="stl_indicatorWithoutPadding" format="boolean"/>
|
||||
<attr name="stl_indicatorInFront" format="boolean"/>
|
||||
<attr name="stl_indicatorInterpolation" format="enum">
|
||||
<enum name="smart" value="0"/>
|
||||
<enum name="linear" value="1"/>
|
||||
</attr>
|
||||
<attr name="stl_indicatorGravity" format="enum">
|
||||
<enum name="bottom" value="0"/>
|
||||
<enum name="top" value="1"/>
|
||||
<enum name="center" value="2"/>
|
||||
</attr>
|
||||
<attr name="stl_indicatorColor" format="color"/>
|
||||
<attr name="stl_indicatorColors" format="reference"/>
|
||||
<!-- 指示器高-->
|
||||
<attr name="stl_indicatorThickness" format="dimension"/>
|
||||
<!-- 指示器宽-->
|
||||
<attr name="stl_indicatorWidth" format="dimension">
|
||||
<enum name="auto" value="-1"/>
|
||||
</attr>
|
||||
<attr name="stl_indicatorCornerRadius" format="dimension"/>
|
||||
|
||||
<attr name="stl_overlineColor" format="color"/>
|
||||
<attr name="stl_overlineThickness" format="dimension"/>
|
||||
|
||||
<attr name="stl_underlineColor" format="color"/>
|
||||
<attr name="stl_underlineThickness" format="dimension"/>
|
||||
|
||||
<attr name="stl_dividerColor" format="color"/>
|
||||
<attr name="stl_dividerColors" format="reference"/>
|
||||
<attr name="stl_dividerThickness" format="dimension"/>
|
||||
|
||||
<attr name="stl_defaultTabBackground" format="reference"/>
|
||||
|
||||
<attr name="stl_defaultTabTextAllCaps" format="boolean"/>
|
||||
<attr name="stl_defaultTabTextColor" format="color|reference"/>
|
||||
<attr name="stl_defaultTabTextSize" format="dimension"/>
|
||||
<attr name="stl_defaultTabTextHorizontalPadding" format="dimension"/>
|
||||
<attr name="stl_defaultTabTextMinWidth" format="dimension"/>
|
||||
|
||||
<!-- 自定义布局-->
|
||||
<attr name="stl_customTabTextLayoutId" format="reference"/>
|
||||
<attr name="stl_customTabTextViewId" format="reference"/>
|
||||
<!-- 权重-->
|
||||
<attr name="stl_distributeEvenly" format="boolean"/>
|
||||
|
||||
<attr name="stl_clickable" format="boolean"/>
|
||||
|
||||
<attr name="stl_titleOffset" format="dimension">
|
||||
<enum name="auto_center" value="-1"/>
|
||||
</attr>
|
||||
|
||||
<attr name="stl_drawDecorationAfterTab" format="boolean"/>
|
||||
|
||||
<attr name="stl_tabTextStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@@ -0,0 +1,434 @@
|
||||
//package com.kelly.dawi.util
|
||||
//
|
||||
//import android.graphics.Bitmap
|
||||
//import android.graphics.Paint
|
||||
//import android.os.Looper
|
||||
//import android.text.TextPaint
|
||||
//import android.view.View
|
||||
//import android.widget.ImageView
|
||||
//import androidx.annotation.DrawableRes
|
||||
//import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
//import androidx.lifecycle.lifecycleScope
|
||||
//import com.bumptech.glide.Glide
|
||||
//import com.bumptech.glide.load.DataSource
|
||||
//import com.bumptech.glide.load.engine.GlideException
|
||||
//import com.bumptech.glide.request.FutureTarget
|
||||
//import com.bumptech.glide.request.RequestFutureTarget
|
||||
//import com.bumptech.glide.request.RequestListener
|
||||
//import com.bumptech.glide.request.RequestOptions
|
||||
//import com.bumptech.glide.request.target.Target
|
||||
//import com.kelly.dawi.dp
|
||||
//import com.kelly.dawi.getOrNull
|
||||
//import com.kelly.dawi.simpleImpl.SimpleSvgaCallback
|
||||
//import com.kelly.dawi.suspendGetOrNull
|
||||
//import com.opensource.svgaplayer.SVGAImageView
|
||||
//import com.opensource.svgaplayer.glideplugin.asSVGADrawable
|
||||
//import com.tencent.qgame.animplayer.AnimView
|
||||
//import com.tencent.qgame.animplayer.inter.IFetchResource
|
||||
//import com.tencent.qgame.animplayer.mix.Resource
|
||||
//import com.tencent.qgame.animplayer.util.ScaleType
|
||||
//import com.kelly.dawi.simpleImpl.SimpleVapCallback
|
||||
//import kotlinx.coroutines.*
|
||||
//import java.io.File
|
||||
//import java.lang.RuntimeException
|
||||
//import java.util.concurrent.CancellationException
|
||||
//
|
||||
///**
|
||||
// * @Author Vance
|
||||
// * @Date 2022/6/17 0017 13:44
|
||||
// */
|
||||
//
|
||||
//object AnimEffectUtil {
|
||||
//
|
||||
// @JvmStatic
|
||||
// fun newBuilder(): ExtBuilder {
|
||||
// return ExtBuilder()
|
||||
// }
|
||||
//
|
||||
// @JvmStatic
|
||||
// fun load(url: String?): ExtBuilder {
|
||||
// return ExtBuilder().load(url)
|
||||
// }
|
||||
//
|
||||
// class ExtBuilder : IFetchResource, View.OnAttachStateChangeListener {
|
||||
// private val defTextPaint = TextPaint().apply { textAlign = Paint.Align.CENTER }
|
||||
//
|
||||
// private var job: Job? = null
|
||||
// private var extMap = mutableMapOf<String, Ext>()
|
||||
// private var glideRequests = mutableListOf<FutureTarget<*>?>()
|
||||
//
|
||||
// private var tempExtMap = mutableMapOf<String, Ext>()
|
||||
// private var listener: Listener? = null
|
||||
// private var repeatCount: Int? = null
|
||||
// private var clearsAfterDetached: Boolean? = null
|
||||
// private var scaleType: ImageView.ScaleType? = null
|
||||
//
|
||||
// private var url: String? = null
|
||||
//
|
||||
// private var skipMemoryCache = true
|
||||
//
|
||||
// private var scope:CoroutineScope? = null
|
||||
//
|
||||
// fun load(url: String?): ExtBuilder {
|
||||
// this.url = url
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// fun setCoroutineScope(scope:CoroutineScope): ExtBuilder {
|
||||
// this.scope = scope
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// fun skipMemoryCache(skipMemoryCache: Boolean): ExtBuilder {
|
||||
// this.skipMemoryCache = skipMemoryCache
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// fun scaleType(scaleType: ImageView.ScaleType): ExtBuilder {
|
||||
// this.scaleType = scaleType
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// fun clearsAfterDetached(clearsAfterDetached: Boolean): ExtBuilder {
|
||||
// this.clearsAfterDetached = clearsAfterDetached
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// fun listener(listener: Listener?): ExtBuilder {
|
||||
// this.listener = listener
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// fun repeatCount(repeatCount: Int): ExtBuilder {
|
||||
// this.repeatCount = repeatCount
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * textPain是SVGA用的
|
||||
// */
|
||||
// @JvmOverloads
|
||||
// fun putText(key: String, text: String?, textPaint: TextPaint = defTextPaint): ExtBuilder {
|
||||
// tempExtMap[key] = Ext(text, textPaint)
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// @JvmOverloads
|
||||
// fun putImg(
|
||||
// key: String,
|
||||
// imgUrl: Any?,
|
||||
// widthDp: Float = 0f,
|
||||
// heightDp: Float = 0f,
|
||||
// isCircle: Boolean = true,
|
||||
// @DrawableRes default: Int = 0
|
||||
// ): ExtBuilder {
|
||||
// tempExtMap[key] = Ext(
|
||||
// imgGetter = {
|
||||
// val option = RequestOptions()
|
||||
// if (isCircle) {
|
||||
// option.circleCrop()
|
||||
// }
|
||||
// if (widthDp > 0 && heightDp > 0) {
|
||||
// option.override(widthDp.dp, heightDp.dp)
|
||||
// }
|
||||
//
|
||||
// val futureTarget = RequestFutureTarget<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
// glideRequests.add(futureTarget)
|
||||
// var bitmap: Bitmap? = null
|
||||
// try {
|
||||
// bitmap = Glide.with(MyUtils.application)
|
||||
// .asBitmap()
|
||||
// .load(imgUrl)
|
||||
// .apply(option)
|
||||
// .addListener(futureTarget)
|
||||
// .into(futureTarget)
|
||||
// .get()
|
||||
// }catch (e: InterruptedException){
|
||||
// glideRequests.remove(futureTarget)
|
||||
// return@Ext null //取消掉的直接返回不去加载默认图了
|
||||
// }catch (e: CancellationException){
|
||||
// glideRequests.remove(futureTarget)
|
||||
// return@Ext null //取消掉的直接返回不去加载默认图了
|
||||
// }catch (e: Exception){
|
||||
// glideRequests.remove(futureTarget)
|
||||
// }
|
||||
//
|
||||
// if (bitmap == null && default != 0) {
|
||||
// val defFutureTarget = RequestFutureTarget<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
// glideRequests.add(defFutureTarget)
|
||||
// bitmap = Glide.with(MyUtils.application)
|
||||
// .asBitmap()
|
||||
// .load(default)
|
||||
// .apply(option)
|
||||
// .addListener(defFutureTarget)
|
||||
// .into(defFutureTarget)
|
||||
// .getOrNull()
|
||||
// }
|
||||
// bitmap
|
||||
// })
|
||||
// return this
|
||||
// }
|
||||
//
|
||||
// fun into(svgaImageView: SVGAImageView){
|
||||
// setup(svgaImageView)
|
||||
// val animUrl = url
|
||||
// if(animUrl.isNullOrBlank()){
|
||||
// listener?.onAnimError(RuntimeException("url is empty"))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// var svgaUrl = animUrl
|
||||
// if(!animUrl.startsWith("http")){//没有http前缀就当成asset文件加载
|
||||
// svgaUrl = "file:///android_asset/$animUrl"
|
||||
// }
|
||||
//
|
||||
// if(scope == null){
|
||||
// scope = svgaImageView.findViewTreeLifecycleOwner()?.lifecycleScope
|
||||
// }
|
||||
//
|
||||
// job = scope?.launch(Dispatchers.IO) {
|
||||
// val svgaJob = async {
|
||||
// Glide.with(svgaImageView.context)
|
||||
// .asSVGADrawable()
|
||||
// .load(svgaUrl.trim())
|
||||
// .skipMemoryCache(skipMemoryCache)
|
||||
// .submit()
|
||||
// .suspendGetOrNull()
|
||||
// }
|
||||
//
|
||||
// val bitmapJobMap = mutableMapOf<String, Deferred<Bitmap?>>()
|
||||
// val textMap = mutableMapOf<String, Ext>()
|
||||
// val paramsMap = mutableMapOf<String, Ext>()
|
||||
// try {
|
||||
// paramsMap.putAll(extMap)
|
||||
// paramsMap.forEach {
|
||||
// ensureActive()
|
||||
// if (it.value.isImg()) {
|
||||
// val job = async { it.value.getBitMap() }
|
||||
// bitmapJobMap[it.key] = job
|
||||
// } else {
|
||||
// textMap[it.key] = it.value
|
||||
// }
|
||||
// }
|
||||
// } catch (_: Exception) { }
|
||||
//
|
||||
// val drawable = svgaJob.await()
|
||||
// if (drawable == null) {
|
||||
// bitmapJobMap.forEach {
|
||||
// it.value.cancelAndJoin()
|
||||
// }
|
||||
// val imgRequests = glideRequests
|
||||
// imgRequests.forEach {
|
||||
// it?.cancel(true)
|
||||
// }
|
||||
// withContext(Dispatchers.Main) {
|
||||
// onAnimError("svga loading error ")
|
||||
// }
|
||||
// } else {
|
||||
// bitmapJobMap.forEach { entry ->
|
||||
// ensureActive()
|
||||
// entry.value.await()?.let {
|
||||
// drawable.dynamicItem.setDynamicImage(it, entry.key)
|
||||
// }
|
||||
// }
|
||||
// textMap.forEach { entry ->
|
||||
// entry.value.text?.let {
|
||||
// drawable.dynamicItem.setDynamicText(it, entry.value.textPaint, entry.key)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// withContext(Dispatchers.Main) {
|
||||
// onAnimStart()
|
||||
// svgaImageView.setImageDrawable(drawable)
|
||||
// svgaImageView.startAnimation()
|
||||
// extMap = mutableMapOf()
|
||||
// glideRequests = mutableListOf()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun into(animView: AnimView){
|
||||
// setup(animView)
|
||||
// val mp4Url = url
|
||||
// if(mp4Url.isNullOrBlank()){
|
||||
// listener?.onAnimError(RuntimeException("url is empty"))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// animView.setFetchResource(this)
|
||||
//
|
||||
// if(!mp4Url.startsWith("http")){//没有http前缀就当成asset文件加载
|
||||
// onAnimStart()
|
||||
// animView.startPlay(animView.context.assets, mp4Url)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if(scope == null){
|
||||
// scope = animView.findViewTreeLifecycleOwner()?.lifecycleScope
|
||||
// }
|
||||
// job = scope?.launch(Dispatchers.IO) {
|
||||
// Glide.with(animView.context)
|
||||
// .asFile()
|
||||
// .skipMemoryCache(skipMemoryCache)
|
||||
// .load(mp4Url)
|
||||
// .addListener(object : RequestListener<File> {
|
||||
// override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<File>, isFirstResource: Boolean): Boolean {
|
||||
// e?.logRootCauses("vap_loading_error")
|
||||
// animView.post {
|
||||
// onAnimError("vap loading error msg:${e?.message ?: "empty"}")
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// override fun onResourceReady(resource: File, model: Any, target: Target<File>?, dataSource: DataSource, isFirstResource: Boolean): Boolean {
|
||||
// return false
|
||||
// }
|
||||
// })
|
||||
// .submit()
|
||||
// .suspendGetOrNull()
|
||||
// ?.let {
|
||||
// withContext(Dispatchers.Main){
|
||||
// onAnimStart()
|
||||
// animView.startPlay(it)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun setup(view: View){
|
||||
// if(Looper.myLooper() != Looper.getMainLooper()){
|
||||
// throw Exception("must run on MAIN Thread current Thread ${Thread.currentThread().name}")
|
||||
// }
|
||||
// cancelJob()
|
||||
// extMap = mutableMapOf()
|
||||
// extMap.putAll(tempExtMap)
|
||||
// view.addOnAttachStateChangeListener(this)
|
||||
//
|
||||
// if(view is SVGAImageView){
|
||||
// if(!skipMemoryCache){
|
||||
// view.clearsAfterDetached = false
|
||||
// } else {
|
||||
// clearsAfterDetached?.let { view.clearsAfterDetached = it }
|
||||
// }
|
||||
// repeatCount?.let { view.loops = if(it <= 0) Int.MAX_VALUE else it }
|
||||
// scaleType?.let { view.scaleType = it }
|
||||
// view.callback = object: SimpleSvgaCallback(){
|
||||
// override fun onStep(frame: Int, percentage: Double) {
|
||||
// if (percentage == 1.0) {
|
||||
// view.post { onAnimComplete() }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }else if (view is AnimView){
|
||||
// repeatCount?.let { view.setLoop(if(it <= 0) Int.MAX_VALUE else it) }
|
||||
// scaleType?.let {
|
||||
// when (it) {
|
||||
// ImageView.ScaleType.FIT_XY -> {
|
||||
// view.setScaleType(ScaleType.FIT_XY)
|
||||
// }
|
||||
// ImageView.ScaleType.CENTER_CROP -> {
|
||||
// view.setScaleType(ScaleType.CENTER_CROP)
|
||||
// }
|
||||
// else -> {
|
||||
// view.setScaleType(ScaleType.FIT_CENTER)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// view.setAnimListener(object : SimpleVapCallback(){
|
||||
// override fun onFailed(errorType: Int, errorMsg: String?) {
|
||||
// view.post { onAnimError("vap errorType:${errorMsg} errorMsg:${errorMsg ?: "empty"}") }
|
||||
// }
|
||||
//
|
||||
// override fun onVideoComplete() {
|
||||
// view.post { onAnimComplete() }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun clear(){
|
||||
// tempExtMap = mutableMapOf()
|
||||
// extMap = mutableMapOf()
|
||||
// cancelJob()
|
||||
// }
|
||||
//
|
||||
// private fun cancelJob(){
|
||||
// try {
|
||||
// job?.cancel()
|
||||
// val imgRequests = glideRequests
|
||||
// glideRequests = mutableListOf()
|
||||
// imgRequests.forEach {
|
||||
// it?.cancel(true)
|
||||
// }
|
||||
// } catch (_: Exception) { }
|
||||
// }
|
||||
//
|
||||
// private fun onAnimStart() {
|
||||
// listener?.onAnimStart()
|
||||
// }
|
||||
//
|
||||
// private fun onAnimComplete() {
|
||||
// extMap = mutableMapOf()
|
||||
// glideRequests = mutableListOf()
|
||||
// listener?.onAnimComplete()
|
||||
// }
|
||||
//
|
||||
// private fun onAnimError(msg: String) {
|
||||
// extMap = mutableMapOf()
|
||||
// cancelJob()
|
||||
// listener?.onAnimError(Exception(msg))
|
||||
// }
|
||||
//
|
||||
// override fun fetchImage(resource: Resource, result: (Bitmap?) -> Unit) {
|
||||
// val ext = extMap[resource.tag]
|
||||
// result(ext?.getBitMap())
|
||||
// }
|
||||
//
|
||||
// override fun fetchText(resource: Resource, result: (String?) -> Unit) {
|
||||
// val ext = extMap[resource.tag]
|
||||
// result(ext?.text)
|
||||
// }
|
||||
//
|
||||
// override fun releaseResource(resources: List<Resource>) {
|
||||
// }
|
||||
//
|
||||
// private inner class Ext(
|
||||
// val text: String? = null,
|
||||
// val textPaint: TextPaint = defTextPaint,
|
||||
// val imgGetter: (() -> Bitmap?)? = null) {
|
||||
// fun getBitMap(): Bitmap? {
|
||||
// return try {
|
||||
// val bitmap = imgGetter?.invoke()
|
||||
// bitmap
|
||||
// } catch (t: Throwable) {
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun isImg(): Boolean {
|
||||
// return text.isNullOrBlank()
|
||||
// }
|
||||
//
|
||||
// fun isText(): Boolean {
|
||||
// return !text.isNullOrBlank()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onViewAttachedToWindow(v: View) {
|
||||
// }
|
||||
//
|
||||
// override fun onViewDetachedFromWindow(v: View) {
|
||||
// v.removeOnAttachStateChangeListener(this)
|
||||
// cancelJob()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// interface Listener {
|
||||
// fun onAnimStart() {}
|
||||
// fun onAnimComplete()
|
||||
// fun onAnimError(e: Exception?) {
|
||||
// onAnimComplete()
|
||||
// }
|
||||
// }
|
||||
//}
|
@@ -1376,6 +1376,28 @@ class GlideUtils {
|
||||
glideConfig?.downloadOnly()?.load(url)?.listener(listener)?.preload()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Glide下载图片,返回File
|
||||
*/
|
||||
fun downloadFromUrl2(
|
||||
context: Context?,
|
||||
url: String?,
|
||||
listener: RequestListener<File?>?
|
||||
) {
|
||||
if (context == null) {
|
||||
LibLogger.error(TAG, "load context is null")
|
||||
return
|
||||
}
|
||||
|
||||
val glideConfig = getGlideConfig(context)
|
||||
glideConfig
|
||||
?.asFile()
|
||||
?.skipMemoryCache(true)
|
||||
?.load(url)
|
||||
?.addListener(listener)
|
||||
?.submit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载图片,并添加RequestListener ,无占位图 ,listener需使用addListener方法添加
|
||||
*/
|
||||
@@ -1422,6 +1444,17 @@ class GlideUtils {
|
||||
.into(imageView)
|
||||
}
|
||||
}
|
||||
//加载图片,无默认图片
|
||||
fun loadAsBitmap(url: String?, imageView: ImageView?) {
|
||||
if (imageView == null || TextUtils.isEmpty(url)) {
|
||||
LibLogger.error(TAG, "load url is invalid or imageViw is null")
|
||||
return
|
||||
}
|
||||
getGlideConfig(imageView.context)?.apply {
|
||||
this.asDrawable().load(url)
|
||||
.into(imageView)
|
||||
}
|
||||
}
|
||||
|
||||
//加载图片,无默认图片
|
||||
fun loadWithError(url: String?, errorDrawable: Int, imageView: ImageView?) {
|
||||
|
@@ -8,6 +8,8 @@ import androidx.core.view.isVisible
|
||||
import com.chwl.library.utils.ResUtil
|
||||
import com.chwl.library.utils.SizeUtils
|
||||
import com.example.lib_utils.UiUtils.isRtl
|
||||
import com.example.lib_utils.ktx.getString
|
||||
import com.hjq.toast.ToastUtils
|
||||
|
||||
|
||||
fun View.setMargin(start:Int?=null,top:Int?=null,end:Int?=null,bottom:Int?=null,isDP:Boolean = true) {
|
||||
@@ -70,6 +72,32 @@ fun String?.isVerify() : Boolean {
|
||||
return this?.isBlank() == false
|
||||
}
|
||||
|
||||
fun String?.isSvgaUrl() : Boolean {
|
||||
if (this.isVerify()) {
|
||||
return this?.endsWith(".svga") == true || this?.endsWith(".SVGA") == true
|
||||
}
|
||||
return false
|
||||
}
|
||||
fun String?.isMp4() : Boolean {
|
||||
if (this.isVerify()) {
|
||||
return this?.endsWith(".mp4") == true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun String?.doToast() {
|
||||
if (this.isVerify()) {
|
||||
ToastUtils.show(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.doToast() {
|
||||
val string = this.getString()
|
||||
if (string.isVerify()) {
|
||||
ToastUtils.show(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun List<Any>?.isVerify() : Boolean{
|
||||
return this?.isEmpty() == false
|
||||
}
|
||||
|
Reference in New Issue
Block a user