feat : 重写 装扮商城 , 我的装扮 , 装扮赠送 页面

This commit is contained in:
eggmanQQQ
2024-11-20 15:06:13 +08:00
parent a48f768434
commit e0b348f92a
98 changed files with 6761 additions and 347 deletions

View File

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

View File

@@ -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?

View File

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

View File

@@ -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 &gt; SlidingTabsBasic &gt;
* <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;
}
}
}
}
}

View File

@@ -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 &gt; SlidingTabsBasic &gt;
* <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;
}
}
}

View File

@@ -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 &gt; SlidingTabsBasic &gt;
* <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;
}
}
}

View File

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

View File

@@ -0,0 +1,8 @@
package com.chwl.library.widget.tab;
import androidx.annotation.Nullable;
public interface TabTitleProvider {
@Nullable
CharSequence getPageTitle(int position);
}

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

View File

@@ -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()
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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>

View File

@@ -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()
// }
// }
//}

View File

@@ -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?) {

View File

@@ -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
}