diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 129fae0ec..d518edcfc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -314,6 +314,12 @@ + + () { + GlideUtils.instance().downloadFromUrl2(getContext(), url, new RequestListener() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { - - log("drawSvgaEffect onDownloadError url:" + url); + String message = ""; + if (e != null) { + message = e.getMessage(); + } + log("drawEffect drawSvgaEffect onDownloadError url:" + url + " error = "+message); effectHandler.sendEmptyMessage(0); return true; } @Override public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + String path = ""; if (resource != null) { - String path = resource.getPath(); - log("drawSvgaEffect onDownloadCompleted url:" + url + " path:" + path); + path = resource.getPath(); drawSvgaEffectFile(path); + } else { + effectHandler.sendEmptyMessage(0); } + log("drawEffect drawSvgaEffect onDownloadCompleted url:" + url + " path:" + path); return true; } + }); @@ -257,23 +264,33 @@ public class GiftEffectView extends RelativeLayout { } } - private void drawVAPEffect(String url) { - log("drawVAPEffect url:" + url); - GlideUtils.instance().downloadFromUrl(getContext(), url, new RequestListener() { + + private void drawVAPEffect(String url) { + log("drawEffect drawVAPEffect url:" + url); + + GlideUtils.instance().downloadFromUrl2(getContext(), url, new RequestListener() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { - log("drawVAPEffect onDownloadError url:" + url); + String message = ""; + if (e != null) { + message = e.getMessage(); + } + log("drawEffect drawVAPEffect onDownloadError url:" + url + " error = "+message); effectHandler.sendEmptyMessageDelayed(0, 4000); return true; } @Override public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + String path = ""; if (resource != null) { - log("drawVAPEffect onDownloadCompleted url:" + url + " path:" + resource.getPath()); + path = resource.getPath(); vapAnimView.startPlay(resource); + } else { + effectHandler.sendEmptyMessage(0); } + log("drawEffect drawVAPEffect onDownloadCompleted url:" + url + " path:" + path); return true; } }); diff --git a/app/src/main/java/com/chwl/app/base/BaseActivity.java b/app/src/main/java/com/chwl/app/base/BaseActivity.java index cce9956f3..1e2adfb3f 100644 --- a/app/src/main/java/com/chwl/app/base/BaseActivity.java +++ b/app/src/main/java/com/chwl/app/base/BaseActivity.java @@ -46,34 +46,11 @@ import androidx.fragment.app.FragmentManager; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.chwl.app.notify.GlobalNotifyManager; -import com.chwl.app.utils.RoomBoomManager; -import com.chwl.core.gift.bean.BoomMsgAnimBean; -import com.chwl.core.gift.bean.BoomMsgDialogBean; -import com.chwl.core.im.custom.bean.BoomMsgAttachment; -import com.chwl.core.manager.AvRoomDataManager; -import com.chwl.core.utils.LogUtils; -import com.chwl.library.language.LanguageHelper; -import com.example.lib_utils.UiUtils; -import com.google.gson.Gson; -import com.netease.nim.uikit.common.util.log.LogUtil; -import com.netease.nimlib.chatroom.model.ChatRoomMessageImpl; -import com.netease.nimlib.sdk.NIMSDK; -import com.netease.nimlib.sdk.Observer; -import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage; -import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum; -import com.netease.nimlib.sdk.msg.model.BroadcastMessage; -import com.netease.nimlib.session.IMMessageImpl; -import com.orhanobut.logger.Logger; -import com.readystatesoftware.systembartint.SystemBarTintManager; -import com.tbruyelle.rxpermissions2.RxPermissions; -import com.trello.rxlifecycle3.android.ActivityEvent; -import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; import com.chwl.app.NimMiddleActivity; import com.chwl.app.R; +import com.chwl.app.application.App; import com.chwl.app.application.IReportConstants; import com.chwl.app.application.ReportManager; -import com.chwl.app.application.App; import com.chwl.app.avroom.activity.AVRoomActivity; import com.chwl.app.common.LoadingFragment; import com.chwl.app.common.NetworkErrorFragment; @@ -83,6 +60,7 @@ import com.chwl.app.common.permission.PermissionActivity; import com.chwl.app.common.widget.StatusLayout; import com.chwl.app.common.widget.dialog.DialogManager; import com.chwl.app.common.widget.dialog.DialogUiHelper; +import com.chwl.app.notify.GlobalNotifyManager; import com.chwl.app.ui.im.avtivity.NimP2PMessageActivity; import com.chwl.app.ui.login.AddUserInfoActivity; import com.chwl.app.ui.login.LoginCodeActivity; @@ -94,14 +72,18 @@ import com.chwl.app.ui.widget.dialog.AllPlayEffectDialog; import com.chwl.app.ui.widget.dialog.AllServiceGiftLevelDialog; import com.chwl.app.ui.widget.dialog.AllServiceVipLevelUPDialog; import com.chwl.app.ui.widget.dialog.OpenNobleGlobalNoticeDialog; +import com.chwl.app.utils.RoomBoomManager; import com.chwl.app.utils.UserUtils; import com.chwl.core.XConstants; import com.chwl.core.bean.BaseProtocol; +import com.chwl.core.gift.bean.BoomMsgDialogBean; +import com.chwl.core.im.custom.bean.BoomMsgAttachment; import com.chwl.core.im.custom.bean.CustomAttachment; import com.chwl.core.im.custom.bean.NotifyH5Info; import com.chwl.core.im.custom.bean.PlayEffectInfo; import com.chwl.core.im.custom.bean.RoomLuckySeaMsgBean; import com.chwl.core.im.custom.bean.TarotMsgBean; +import com.chwl.core.manager.AvRoomDataManager; import com.chwl.core.manager.IMNetEaseManager; import com.chwl.core.manager.RoomEvent; import com.chwl.core.mentoring_relationship.event.GrabApprenticesEvent; @@ -115,8 +97,10 @@ import com.chwl.core.pay.bean.WalletInfo; import com.chwl.core.room.model.AvRoomModel; import com.chwl.core.treasurefairy.bean.FairyMsgInfoBean; import com.chwl.core.user.UserModel; +import com.chwl.core.utils.LogUtils; import com.chwl.core.utils.net.RxHelper; import com.chwl.core.vip.bean.VipMessageInfo; +import com.chwl.library.language.LanguageHelper; import com.chwl.library.rxbus.RxBus; import com.chwl.library.utils.NetworkUtils; import com.chwl.library.utils.ResUtil; @@ -125,6 +109,22 @@ import com.chwl.library.utils.UIUtils; import com.chwl.library.utils.codec.DESUtils; import com.chwl.library.utils.config.BasicConfig; import com.chwl.library.utils.log.MLog; +import com.example.lib_utils.UiUtils; +import com.google.gson.Gson; +import com.netease.nim.uikit.StatusBarUtil; +import com.netease.nim.uikit.common.util.log.LogUtil; +import com.netease.nimlib.chatroom.model.ChatRoomMessageImpl; +import com.netease.nimlib.sdk.NIMSDK; +import com.netease.nimlib.sdk.Observer; +import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage; +import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum; +import com.netease.nimlib.sdk.msg.model.BroadcastMessage; +import com.netease.nimlib.session.IMMessageImpl; +import com.orhanobut.logger.Logger; +import com.readystatesoftware.systembartint.SystemBarTintManager; +import com.tbruyelle.rxpermissions2.RxPermissions; +import com.trello.rxlifecycle3.android.ActivityEvent; +import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -276,7 +276,7 @@ public abstract class BaseActivity extends RxAppCompatActivity } } - public void initBlackTitleBar(String title) { + public void initDarkTitleBar(String title) { mTitleBar = findViewById(R.id.title_bar); if (mTitleBar != null) { mTitleBar.setTitle(title); @@ -285,6 +285,21 @@ public abstract class BaseActivity extends RxAppCompatActivity mTitleBar.setLeftImageResource(R.drawable.icon_user_back); mTitleBar.setBackgroundResource(R.color.transparent); mTitleBar.setLeftClickListener(v -> onLeftClickListener()); + StatusBarUtil.StatusBarLightMode(this,false); + StatusBarUtil.transparencyBar(this); + } + } + public void initLightTitleBar(String title) { + mTitleBar = findViewById(R.id.title_bar); + if (mTitleBar != null) { + mTitleBar.setTitle(title); + mTitleBar.setImmersive(false); + mTitleBar.setTitleColor(getResources().getColor(R.color.text_title_color)); + mTitleBar.setLeftImageResource(R.drawable.arrow_left); + mTitleBar.setBackgroundResource(R.color.transparent); + mTitleBar.setLeftClickListener(v -> onLeftClickListener()); + StatusBarUtil.StatusBarLightMode(this,true); + StatusBarUtil.transparencyBar(this); } } diff --git a/app/src/main/java/com/chwl/app/base/BaseDialogFragment.kt b/app/src/main/java/com/chwl/app/base/BaseDialogFragment.kt index d1b84d433..40140b629 100644 --- a/app/src/main/java/com/chwl/app/base/BaseDialogFragment.kt +++ b/app/src/main/java/com/chwl/app/base/BaseDialogFragment.kt @@ -5,7 +5,11 @@ import android.content.DialogInterface import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.view.* +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner @@ -27,6 +31,7 @@ abstract class BaseDialogFragment : RxDialogFragment() { open var height = WindowManager.LayoutParams.WRAP_CONTENT open var gravity = Gravity.CENTER open var dimAmount = 0.5f + var mActionCallBack : Action?= null override fun onStart() { super.onStart() @@ -120,4 +125,8 @@ abstract class BaseDialogFragment : RxDialogFragment() { } abstract fun init() + + public interface Action{ + fun onAction(type:Int,data:D?) + } } \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/base/BaseListFragment.kt b/app/src/main/java/com/chwl/app/base/BaseListFragment.kt new file mode 100644 index 000000000..e3702379b --- /dev/null +++ b/app/src/main/java/com/chwl/app/base/BaseListFragment.kt @@ -0,0 +1,108 @@ +package com.chwl.app.base + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chwl.app.R +import com.chwl.app.bindadapter.BaseBindingAdapter +import com.chwl.app.databinding.BaseListFragmentBinding +import com.chwl.library.common.util.setVis +import com.scwang.smartrefresh.layout.api.RefreshLayout +import com.scwang.smartrefresh.layout.listener.OnRefreshLoadmoreListener + +abstract class BaseListFragment: + BaseViewBindingFragment() { + + lateinit var mAdapter: BaseBindingAdapter<*, D> + protected var mPageSize = 20; + protected var mPage = 1; + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + _binding = BaseListFragmentBinding.inflate(layoutInflater) + return binding.root + } + + + + override fun init() { + mAdapter = getAdapter() + + binding.rvList.layoutManager = getLayoutManager() + binding.rvList.adapter = mAdapter + + binding.srlLayout.setOnRefreshLoadmoreListener(object : OnRefreshLoadmoreListener { + override fun onRefresh(refreshlayout: RefreshLayout?) { + onRefresh() + } + + override fun onLoadmore(refreshlayout: RefreshLayout?) { + onLoad() + } + }) + mAdapter.onItemClickListener = BaseQuickAdapter.OnItemClickListener { adapter, view, position -> + onItemClicks(mAdapter, view, position) + } + mAdapter.onItemChildClickListener = BaseQuickAdapter.OnItemChildClickListener { adapter, view, position -> + onItemChildClicks(mAdapter, view, position) + } + initView() + } + + abstract fun getAdapter(): BaseBindingAdapter<*, D> + + protected open fun getLayoutManager(): RecyclerView.LayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) + protected open fun hasTitle(): Boolean = false + + protected open fun onItemClicks( + adapter: BaseBindingAdapter<*, D>?, + view: View?, + position: Int + ) {} + protected open fun onItemChildClicks( + adapter: BaseBindingAdapter<*, D>?, + view: View?, + position: Int + ) {} + + abstract fun onLoad() + abstract fun onRefresh() + + + abstract fun initView() + + protected open fun stopAnim() { + binding.srlLayout.finishLoadmore() + binding.srlLayout.finishRefresh() + } + + var mTitleBar : TitleBar?=null + open fun initWhiteTitleBar(title: String?,listener:View.OnClickListener) { + mTitleBar = _binding?.root?.findViewById(R.id.title_bar) + if (mTitleBar != null) { + mTitleBar?.setTitle(title) + mTitleBar?.setImmersive(false) + mTitleBar?.setTitleColor(resources.getColor(R.color.text_title_color)) + mTitleBar?.setLeftImageResource(R.drawable.arrow_left) + mTitleBar?.setBackgroundResource(R.color.transparent) + mTitleBar?.setLeftClickListener(listener) + mTitleBar?.setVis(true) + } + } + + fun initBlackTitleBar(title: String?,listener:View.OnClickListener) { + mTitleBar = _binding?.root?.findViewById(R.id.title_bar) + if (mTitleBar != null) { + mTitleBar?.setTitle(title) + mTitleBar?.setImmersive(false) + mTitleBar?.setTitleColor(resources.getColor(R.color.white)) + mTitleBar?.setLeftImageResource(R.drawable.icon_user_back) + mTitleBar?.setBackgroundResource(R.color.transparent) + mTitleBar?.setLeftClickListener(listener) + mTitleBar?.setVis(true) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/base/BaseViewBindingActivity.kt b/app/src/main/java/com/chwl/app/base/BaseViewBindingActivity.kt index 3c301a5cc..29b801b51 100644 --- a/app/src/main/java/com/chwl/app/base/BaseViewBindingActivity.kt +++ b/app/src/main/java/com/chwl/app/base/BaseViewBindingActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import androidx.annotation.Nullable import androidx.viewbinding.ViewBinding +import com.netease.nim.uikit.StatusBarUtil import java.lang.reflect.ParameterizedType @@ -20,6 +21,11 @@ abstract class BaseViewBindingActivity : BaseActivity(){ val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java) binding = method.invoke(null,layoutInflater) as T setContentView(binding.root) + + if (transparencyBar()) { + StatusBarUtil.transparencyBar(this) + } + init() } @@ -29,4 +35,5 @@ abstract class BaseViewBindingActivity : BaseActivity(){ protected open fun initBefore(@Nullable savedInstanceState: Bundle?) {} abstract fun init() + protected open fun transparencyBar():Boolean=false } \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/bindadapter/BaseBindingAdapter.kt b/app/src/main/java/com/chwl/app/bindadapter/BaseBindingAdapter.kt index b36b29b3c..0329e2396 100644 --- a/app/src/main/java/com/chwl/app/bindadapter/BaseBindingAdapter.kt +++ b/app/src/main/java/com/chwl/app/bindadapter/BaseBindingAdapter.kt @@ -15,8 +15,8 @@ open class BaseBindingAdapter() : BaseQuickAdapter - val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java) - val binding = method.invoke(null, mLayoutInflater) as VB + val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java,ViewGroup::class.java, Boolean::class.java) + val binding = method.invoke(null, mLayoutInflater,parent,false) as VB val view = binding?.root?:return View(mContext) view.setTag(R.id.BaseQuickAdapter_databinding_support, binding) return view diff --git a/app/src/main/java/com/chwl/app/decoration/adapter/DressUpAdapter.kt b/app/src/main/java/com/chwl/app/decoration/adapter/DressUpAdapter.kt new file mode 100644 index 000000000..6cfe3b80f --- /dev/null +++ b/app/src/main/java/com/chwl/app/decoration/adapter/DressUpAdapter.kt @@ -0,0 +1,99 @@ +package com.chwl.app.decoration.adapter + +import android.view.Gravity +import com.chwl.app.R +import com.chwl.app.bindadapter.BaseBindingAdapter +import com.chwl.app.bindadapter.BaseBindingViewHolder +import com.chwl.app.databinding.ItemDressUpLayoutBinding +import com.chwl.app.decoration.ui.activity.DressUpTabActivity +import com.chwl.app.decoration.util.DressUpUtil +import com.chwl.core.decoration.bean.DressUpInfo +import com.chwl.library.common.util.isVerify +import com.chwl.library.common.util.setVis +import com.example.lib_utils.ktx.getString + +class DressUpAdapter : BaseBindingAdapter() { + + override fun convert( + helper: BaseBindingViewHolder, + item: DressUpInfo + ) { + val binding = helper.binding + setViewDef(binding) + + + + if (item.isSelect) { + binding.root.setBackgroundResource(R.drawable.ic_dress_item_s) + } else { + binding.root.setBackgroundResource(R.drawable.ic_dress_item_n) + } + + if (item.isNull) { + binding.empty.setVis(true) + binding.name.text = R.string.Not_used.getString() + binding.name.setVis(true) + return + } + + DressUpUtil.setItemPic(item,binding.picLayout) + binding.picLayout.setVis(true) + + + binding.name.text = item.name + binding.name.gravity = if (item.isMy) Gravity.CENTER else Gravity.START + binding.name.setVis(true) + + + DressUpUtil.setTextPrice(binding.price,item) + binding.price.setVis(!item.isMy) + + + binding.discount.text = item.discount.toString() + binding.discount.setVis(!item.isMy) + + + if (item.dressType != DressUpTabActivity.TAB_USER_CARD && item.effect.isVerify()) { + binding.btnPlay.setVis(true) + } else { + binding.btnPlay.setVis(false) + } + + if (item.isMy) { + if (item.isHasExpired) { + binding.limit.setVis(true) + binding.limit.text = R.string.Expired.getString() + } else { + if (item.expireDays != -1) { + binding.limit.text = R.string.sDays.getString(item.expireDays) + } else { + binding.limit.text = R.string.unDay.getString() + } + } + } else { + if (item.dressLimitStatus != 0) { + binding.limit.setVis(true) + } else { + binding.limit.setVis(false) + } + } + + + + + helper.addOnClickListener(R.id.btnPlay) + + } + + private fun setViewDef(binding : ItemDressUpLayoutBinding) { +// binding.bg.setVis(false) +// binding.picBg.setVis(false) + binding.picLayout.setVis(false) +// binding.name.setVis(false) + binding.price.setVis(false) + binding.discount.setVis(false) + binding.empty.setVis(false) + binding.btnPlay.setVis(false) + binding.limit.setVis(false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/decoration/adapter/MyUserCardWearAdapter.java b/app/src/main/java/com/chwl/app/decoration/adapter/MyUserCardWearAdapter.java index 11db79b8d..16bb7c0a8 100644 --- a/app/src/main/java/com/chwl/app/decoration/adapter/MyUserCardWearAdapter.java +++ b/app/src/main/java/com/chwl/app/decoration/adapter/MyUserCardWearAdapter.java @@ -37,7 +37,7 @@ public class MyUserCardWearAdapter extends BaseAdapter { ItemMyUserCardWearBinding binding = (ItemMyUserCardWearBinding) helper.getBinding(); binding.ivUserCardWearMp4.setLoop(Integer.MAX_VALUE); - GlideUtils.instance().downloadFromUrl(binding.ivUserCardWearMp4.getContext(), item.getPic(), new RequestListener() { + GlideUtils.instance().downloadFromUrl2(binding.ivUserCardWearMp4.getContext(), item.getPic(), new RequestListener() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { ImageLoadUtils.loadImage(mContext, item.getPic(), binding.ivUserCardWear); diff --git a/app/src/main/java/com/chwl/app/decoration/ui/DressUpDialog.kt b/app/src/main/java/com/chwl/app/decoration/ui/DressUpDialog.kt new file mode 100644 index 000000000..b1f3ba5cc --- /dev/null +++ b/app/src/main/java/com/chwl/app/decoration/ui/DressUpDialog.kt @@ -0,0 +1,137 @@ +package com.chwl.app.decoration.ui + +import android.view.Gravity +import com.chwl.app.R +import com.chwl.app.base.BaseDialogFragment +import com.chwl.app.databinding.DialogDressUpBinding +import com.chwl.app.decoration.util.DressUpUtil +import com.chwl.core.bean.response.ServiceResult +import com.chwl.core.decoration.bean.DressUpInfo +import com.chwl.core.utils.net.RxHelper +import com.chwl.library.common.util.ClickUtils.click +import com.chwl.library.common.util.doToast +import com.chwl.library.net.rxnet.RxNet +import com.example.lib_utils.ktx.dp +import com.example.lib_utils.ktx.getString +import com.hjq.toast.ToastUtils +import io.reactivex.Single +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.POST + +class DressUpDialog : BaseDialogFragment() { + + override var width = 284.dp + override var height = 353.dp + override var dimAmount = 0.3f + override var gravity = Gravity.CENTER + + + var mDressUpInfo : DressUpInfo? = null + var mTargetUid = -1L + + override fun init() { + + mDressUpInfo?.let { + binding.name.text = it.name + if (it.vipLimit == 0 && it.obtainWay == 1){ + DressUpUtil.setTextPrice(binding.price,it) + }else{ + if (it.obtainWay != 1) { + binding.price.text = R.string.obtainWayAction.getString() + }else if (it.vipLimit != 0) { + binding.price.text = R.string.vipLimit.getString(it.vipLimit) + } + } + + DressUpUtil.setItemPic(it,binding.picLayout) + + } + + binding.btnBuy.text = if (mTargetUid != -1L) R.string.fairy_give.getString() else R.string.buy.getString() + + binding.btnBuy.click { + mDressUpInfo?.let { + if (mTargetUid != -1L) { + postShopGive(it.dressType,it.id,mTargetUid) + .compose(bindToLifecycle()) + .doOnSuccess{ + ToastUtils.show(R.string.doSuccess) + dismiss() + } + .doOnError { + it?.message?.doToast() + } + .subscribe() + } else { + postShopBuy(it.dressType,it.id) + .compose(bindToLifecycle()) + .doOnSuccess{ + ToastUtils.show(R.string.doSuccess) + dismiss() + } + .doOnError { + it?.message?.doToast() + } + .subscribe() + } + } + + } + binding.btnCancel.click { + dismiss() + } + + + + } + + + private fun postShopBuy(dressType: Int,id: Int): Single { + return api.postShopBuy(dressType,id) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + + private fun postShopGive(dressType: Int,id: Int,targetUid:Long): Single { + return api.postShopGive(dressType,id,targetUid) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + private val api: Api = RxNet.create(Api::class.java); + interface Api { + + /** + * 购买装扮 + * + * @return + */ + @FormUrlEncoded + @POST("/dress/shop/buy") + fun postShopBuy( + @Field("dressType") dressType: Int, + @Field("id") id: Int + ): Single> + + + + /** + * 赠送装扮 + * + * @return + */ + @FormUrlEncoded + @POST("/dress/shop/give") + fun postShopGive( + @Field("dressType") dressType: Int, + @Field("id") id: Int, + @Field("targetUid") targetUid: Long, + ): Single> + + + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/decoration/ui/activity/DressUpTabActivity.kt b/app/src/main/java/com/chwl/app/decoration/ui/activity/DressUpTabActivity.kt new file mode 100644 index 000000000..db8d6eec4 --- /dev/null +++ b/app/src/main/java/com/chwl/app/decoration/ui/activity/DressUpTabActivity.kt @@ -0,0 +1,213 @@ +package com.chwl.app.decoration.ui.activity + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.viewpager2.widget.ViewPager2 +import com.alibaba.fastjson.JSON +import com.chwl.app.R +import com.chwl.app.base.BaseViewBindingActivity +import com.chwl.app.databinding.ActivityDressUpTabBinding +import com.chwl.app.databinding.TabDressUpLayoutBinding +import com.chwl.app.decoration.ui.DressUpDialog +import com.chwl.app.decoration.ui.fragment.DressUpMyFragment +import com.chwl.app.decoration.ui.fragment.DressUpStoreFragment +import com.chwl.app.decoration.util.DressUpUtil +import com.chwl.app.event.DressUpEvent +import com.chwl.app.ui.search.SearchUserActivity +import com.chwl.app.ui.utils.loadAvatar +import com.chwl.app.utils.AvatarHelper.loadAvatarFrame +import com.chwl.core.auth.AuthModel +import com.chwl.core.decoration.bean.DressUpInfo +import com.chwl.core.home.bean.TabInfo +import com.chwl.core.user.UserModel +import com.chwl.library.common.util.ClickUtils.click +import com.chwl.library.common.util.setVis +import com.chwl.library.utils.ResUtil +import com.chwl.library.widget.tab.SmartTabLayout2 +import com.chwl.library.widget.tab.TabTitleProvider +import com.chwl.library.widget.tab.util.FragmentPageAdapter +import com.example.lib_utils.ktx.getString +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +class DressUpTabActivity : BaseViewBindingActivity() { + + private var mDressUpInfo : DressUpInfo ? = null + + + + companion object{ + const val TAB_HEAD_WEAR = 0 + const val TAB_CAR = 1 + const val TAB_NAMEPLATE = 2 + const val TAB_USER_CARD = 3 + const val TAB_CHAT_BUBBLE = 4 + const val TAB_USER_INFO_BG = 5 + + fun start(context: Context,isMy:Boolean) { + val intent = Intent(context, DressUpTabActivity::class.java) + intent.putExtra("isMy",isMy) + context.startActivity(intent) + } + } + + private val isMy : Boolean by lazy { intent.getBooleanExtra("isMy",false) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + EventBus.getDefault().register(this) + } + + override fun transparencyBar() = true + + override fun init() { + + if (isMy) { + binding.infoLayout.setVis(true) + binding.btnMyDressUp.setVis(false) + binding.avatar.loadAvatar(UserModel.get().cacheLoginUserInfo?.avatar) + initDarkTitleBar(R.string.decoration_view_mydecorationactivity_01.getString()) + } else { + binding.infoLayout.setVis(false) + binding.btnMyDressUp.setVis(true) + initDarkTitleBar(R.string.decoration_view_decorationstoreactivity_01.getString()) + } + + initTab() + initListener() + + if (isMy) { + UserModel.get().getUserInfo(AuthModel.get().currentUid) + .compose(bindToLifecycle()) + .doOnSuccess { + it?.userHeadwear?.let { head-> + loadAvatarFrame( + binding.avatarHead, + head.getFirstUrl(), + head.type + ) + } + }.subscribe() + } + + } + + val fgLists = arrayListOf() + val tabList = arrayListOf() + private fun initTab() { + + tabList.clear() + tabList.add(TabInfo(TAB_CAR, ResUtil.getString(R.string.decoration_view_decorationstoreactivity_04),R.drawable.ic_dress_tab_car)) + tabList.add(TabInfo(TAB_HEAD_WEAR, ResUtil.getString(R.string.decoration_view_decorationstoreactivity_03),R.drawable.ic_dress_tab_head)) + tabList.add(TabInfo(TAB_NAMEPLATE, ResUtil.getString(R.string.decoration_view_decorationstoreactivity_05),R.drawable.ic_dress_tab_nameplate)) + tabList.add(TabInfo(TAB_USER_CARD, ResUtil.getString(R.string.decoration_view_decorationstoreactivity_06),R.drawable.ic_dress_tab_card)) + tabList.add(TabInfo(TAB_CHAT_BUBBLE, ResUtil.getString(R.string.decoration_view_decorationstoreactivity_07),R.drawable.ic_dress_tab_chat)) + tabList.add(TabInfo(TAB_USER_INFO_BG, ResUtil.getString(R.string.userInfoBg),R.drawable.ic_dress_tab_homepage)) + + fgLists.clear() + tabList.forEach { + if (isMy) { + fgLists.add(DressUpMyFragment.newInstance(it.id)) + } else { + fgLists.add(DressUpStoreFragment.newInstance(it.id)) + } + } + + binding.tabLayout.setCustomTabView(SmartTabLayout2.TabProvider { container, position, pageTitle -> + val tabView = TabDressUpLayoutBinding.inflate(this@DressUpTabActivity.layoutInflater,container,false) + tabView.tabText.text = tabList.get(position).name + tabView.tabImg.setImageResource(tabList.get(position).res) +// tabView.root.layoutParams = LinearLayout.LayoutParams( +// LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT +// ) + return@TabProvider tabView.root + }) + + val pageAdapter = FragmentPageAdapter(this,fgLists) + binding.tabLayout.setViewPager(binding.viewPage,pageAdapter,object : TabTitleProvider { + override fun getPageTitle(position: Int): CharSequence? { + return tabList.get(position).name + } + }) + + binding.viewPage.offscreenPageLimit = 6 + binding.viewPage.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + selectTab(position) + mDressUpInfo = null + binding.bottomView.visibility = View.INVISIBLE + } + }) + + } + + private fun initListener() { + binding.btnBuy.click { + mDressUpInfo?.let { + val dressUpDialog = DressUpDialog() + dressUpDialog.mDressUpInfo = it + dressUpDialog.show(context) + } + + } + + binding.btnGive.click { + mDressUpInfo?.let { + SearchUserActivity.start(context,JSON.toJSONString(it)) + } + } + + binding.btnMyDressUp.click { + DressUpTabActivity.start(context,true) + } + + binding.playLayout.click { + binding.playLayout.setVis(false) + binding.playLayout.removeAllViews() + } + } + + + + + private fun selectTab(pos: Int) { + for (i in 0 until tabList.size) { + val tabAt = binding.tabLayout.getTabAt(i) + val tabView = TabDressUpLayoutBinding.bind(tabAt) + tabView.tabBg.setBackgroundResource(if (pos == i) R.drawable.ic_dress_tab_bg_s else R.drawable.ic_dress_tab_bg_n) + tabView.tabText.alpha = if (pos == i) 1f else 0.5f + } + } + + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onDressUpEvent(event: DressUpEvent) { + val data = event.data + when (event.action) { + DressUpEvent.Action.Select -> { + mDressUpInfo = data + binding.bottomView.setVis(true) + DressUpUtil.setTextPrice(binding.price,data) + if (isMy) { + if (data.dressType == TAB_HEAD_WEAR) { + binding.avatarHead.loadUrl(data.effect) + } + } + } + + DressUpEvent.Action.Play -> { + DressUpUtil.play(data,binding.playLayout) + } + + else -> {} + } + } + + override fun onDestroy() { + super.onDestroy() + EventBus.getDefault().unregister(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/decoration/ui/fragment/DressUpMyFragment.kt b/app/src/main/java/com/chwl/app/decoration/ui/fragment/DressUpMyFragment.kt new file mode 100644 index 000000000..91792aa86 --- /dev/null +++ b/app/src/main/java/com/chwl/app/decoration/ui/fragment/DressUpMyFragment.kt @@ -0,0 +1,490 @@ +package com.chwl.app.decoration.ui.fragment + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.chwl.app.R +import com.chwl.app.base.BaseListFragment +import com.chwl.app.bindadapter.BaseBindingAdapter +import com.chwl.app.common.EmptyViewHelper +import com.chwl.app.decoration.adapter.DressUpAdapter +import com.chwl.app.decoration.ui.activity.DressUpTabActivity +import com.chwl.app.decoration.util.DressUpUtil +import com.chwl.app.event.DressUpEvent +import com.chwl.core.auth.AuthModel +import com.chwl.core.bean.response.ServiceResult +import com.chwl.core.decoration.bean.DressUpInfo +import com.chwl.core.decoration.bean.ShopMine +import com.chwl.core.decoration.car.bean.CarInfo +import com.chwl.core.decoration.headwear.bean.ChatBubbleInfo +import com.chwl.core.decoration.headwear.bean.UserCardWearInfo +import com.chwl.core.decoration.nameplate.bean.NamePlateInfo +import com.chwl.core.manager.AvRoomDataManager +import com.chwl.core.manager.IMNetEaseManager +import com.chwl.core.user.UserModel +import com.chwl.core.utils.net.RxHelper +import com.chwl.library.common.util.doToast +import com.chwl.library.common.util.isVerify +import com.chwl.library.common.util.setMargin +import com.chwl.library.net.rxnet.RxNet +import com.example.lib_utils.ktx.getString +import io.reactivex.Single +import org.greenrobot.eventbus.EventBus +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Query + +class DressUpMyFragment : BaseListFragment() { + + companion object { + fun newInstance(dressType: Int): DressUpMyFragment { + val args = Bundle() + args.putInt("dressType", dressType) + val fragment = DressUpMyFragment() + fragment.arguments = args + return fragment + } + } + + private val mDressType: Int by lazy { requireArguments().getInt("dressType") } + private var mPosition = -1 + + + override fun initView() { + + mAdapter.setEmptyView( + EmptyViewHelper.createEmptyTextViewHeight( + context, + R.string.empty_data.getString() + ) + ) + binding.rvList.setMargin(start = 10, end = 10) + + mPosition = -1 + mAdapter.setNewData(arrayListOf()) + } + + override fun onResume() { + super.onResume() + if (!mAdapter?.data.isVerify()){ + onRefresh() + } + } + + override fun getAdapter(): BaseBindingAdapter<*, DressUpInfo> { + return DressUpAdapter() + } + + override fun getLayoutManager(): RecyclerView.LayoutManager { + return GridLayoutManager(context, 2, RecyclerView.VERTICAL, false) + } + + + override fun onLoad() { + loadDataByType(false) + } + + + + override fun onRefresh() { + loadDataByType(true) + } + + private fun loadDataByType(isRefresh: Boolean) { + + if (isRefresh){ + mPage = 1 + } + + when (mDressType) { + + DressUpTabActivity.TAB_CHAT_BUBBLE -> { + getChatBubbleInfoList() + .compose(bindToLifecycle()) + .doOnSuccess { + it?.let { + val newData = mutableListOf() + it.forEach { + newData.add(DressUpUtil.tranData(it)) + } + setData(newData) + } + }.doOnError { + stopAnim() + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_USER_CARD -> { + getMyUserCardWearList() + .compose(bindToLifecycle()) + .doOnSuccess { + it?.let { + val newData = mutableListOf() + it.forEach { + newData.add(DressUpUtil.tranData(it)) + } + setData(newData) + } + }.doOnError { + stopAnim() + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_NAMEPLATE -> { + getNamePlateList() + .compose(bindToLifecycle()) + .doOnSuccess { + it?.let { + val newData = mutableListOf() + it.nameplateList.forEach { + newData.add(DressUpUtil.tranData(it)) + } + setData(newData) + } + }.doOnError { + stopAnim() + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_HEAD_WEAR , DressUpTabActivity.TAB_USER_INFO_BG -> { + getShopMine() + .compose(bindToLifecycle()) + .doOnSuccess { + it?.let { + val newData = mutableListOf() + it.forEach { + newData.add(DressUpUtil.tranData(it)) + } + setData(newData) + } + }.doOnError { + stopAnim() + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_CAR -> { + getUserCarsV2() + .compose(bindToLifecycle()) + .doOnSuccess { + it?.let { + val newData = mutableListOf() + it.forEach { + newData.add(DressUpUtil.tranData(it)) + } + setData(newData) + } + }.doOnError { + stopAnim() + it?.message?.doToast() + }.subscribe() + } + + else -> {} + } + + } + + private fun userByType( + data: DressUpInfo, + adapter: BaseBindingAdapter<*, DressUpInfo>?, + position: Int + ) { + when (mDressType) { + + DressUpTabActivity.TAB_CHAT_BUBBLE -> { + userChatBubble(data.dressId) + .compose(bindToLifecycle()) + .doOnSuccess { + //更新用户信息 + UserModel.get().updateCurrentUserInfo().subscribe() + setSelect(adapter, position) + } + .doOnError { + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_USER_CARD -> { + userMyUserCardWear(data.dressId) + .compose(bindToLifecycle()) + .doOnSuccess { + //更新用户信息 + UserModel.get().updateCurrentUserInfo().subscribe() + setSelect(adapter, position) + } + .doOnError { + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_NAMEPLATE -> { + useMyNamePlate(data.id,data.word) + .compose(bindToLifecycle()) + .doOnSuccess { + if (AvRoomDataManager.get().mCurrentRoomInfo != null) { + // 更新房间信息 + IMNetEaseManager.get().updateMyRoomRole() + } + //更新用户信息 + UserModel.get().updateCurrentUserInfo().subscribe() + setSelect(adapter, position) + } + .doOnError { + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_HEAD_WEAR , DressUpTabActivity.TAB_USER_INFO_BG-> { + postOptDress(data.dressType, data.dressId) + .compose(bindToLifecycle()) + .doOnSuccess { + //更新用户信息 + UserModel.get().updateCurrentUserInfo().subscribe() + setSelect(adapter, position) + } + .doOnError { + it?.message?.doToast() + }.subscribe() + } + + DressUpTabActivity.TAB_CAR -> { + driveThisCar(data.dressId) + .compose(bindToLifecycle()) + .doOnSuccess { + //更换座驾状态,需要更新缓存 + UserModel.get().onlyUpdateLoginUserInfoCache() + setSelect(adapter, position) + } + .doOnError { + it?.message?.doToast() + }.subscribe() + } + + else -> {} + } + } + + private fun setData(newData:MutableList) { + if (mPage == 1) { + val find = newData.indexOfFirst { it.isSelect() } + newData.add(0,DressUpUtil.getEmpty(find == -1)) + mPosition= if (find==-1) 0 else find+1 + mAdapter.setNewData(newData) + } else { + mAdapter.addData(newData) + } + + if (newData.size >= mPageSize) { + mPage++ + binding.srlLayout.isEnableLoadmore = true + } else { + binding.srlLayout.isEnableLoadmore = false + } + stopAnim() + } + + private fun setSelect(adapter: BaseBindingAdapter<*, DressUpInfo>?, position: Int) { + if (mPosition != -1) { + adapter?.data?.getOrNull(mPosition)?.isSelect = false + adapter?.notifyItemChanged(mPosition) + } + + mPosition = position + adapter?.data?.getOrNull(mPosition)?.isSelect = true + adapter?.notifyItemChanged(mPosition) + R.string.doSuccess.doToast() + } + + override fun onItemChildClicks( + adapter: BaseBindingAdapter<*, DressUpInfo>?, + view: View?, + position: Int + ) { + view?.id?.let { + when (it) { + R.id.btnPlay -> { + adapter?.data?.getOrNull(position)?.let { data -> + EventBus.getDefault().post(DressUpEvent(DressUpEvent.Action.Play, data)) + } + } + else -> {} + } + } + } + + override fun onItemClicks( + adapter: BaseBindingAdapter<*, DressUpInfo>?, + view: View?, + position: Int + ) { + adapter?.data?.getOrNull(position)?.let { data -> + EventBus.getDefault().post(DressUpEvent(DressUpEvent.Action.Select, data)) + if (!data.isSelect() && !data.isHasExpired){ + userByType(data,adapter, position) + } + } + } + + + private fun getShopMine(): Single> { + return api.getShopMine(mDressType,mPage,mPageSize) + .compose(RxHelper.handleBeanData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun postOptDress(dressType: Int,dressId: Int): Single { + return api.postOptDress(dressType,dressId) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun getUserCarsV2(): Single> { + return api.requestMyCarsV2(AuthModel.get().currentUid) + .compose(RxHelper.handleBeanData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun driveThisCar(carId: Int): Single { + val ticket = AuthModel.get().ticket + val uid = AuthModel.get().currentUid + return api.driveThisCar(ticket, uid, carId) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun getNamePlateList(): Single { + return api.getNamePlateList(AuthModel.get().currentUid.toString(), mPage.toString(), mPageSize.toString()) + .compose(RxHelper.handleBeanData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun useMyNamePlate(id: Int,word: String): Single { + val ticket = AuthModel.get().ticket + val uid = AuthModel.get().currentUid + return api.useMyNamePlate(ticket, uid.toString(), id.toString(),word) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun userMyHeadWear(id: Int): Single { + val ticket = AuthModel.get().ticket + val uid = AuthModel.get().currentUid + return api.userMyHeadWear(ticket, uid.toString(), id.toString()) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun getMyUserCardWearList(): Single> { + return api.getMyUserCardWearList(mPage,mPageSize) + .compose(RxHelper.handleBeanData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun userMyUserCardWear(id: Int): Single { + return api.userMyUserCardWear(id.toString()) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + + private fun getChatBubbleInfoList(): Single> { + return api.getChatBubbleInfoList(mPage,mPageSize) + .compose(RxHelper.handleBeanData()) + .compose(RxHelper.handleSchedulers()) + } + + private fun userChatBubble(id: Int): Single { + return api.userChatBubble(id.toString()) + .compose(RxHelper.handleStringData()) + .compose(RxHelper.handleSchedulers()) + } + + + private val api: Api = RxNet.create(Api::class.java); + + interface Api { + + /** + * 获取 我的装扮 : dressType : 5-个人主页背景 + */ + @GET("/dress/shop/mine") + fun getShopMine(@Query("dressType") dressType: Int,@Query("page") page: Int,@Query("pageSize") pageSize: Int): Single>> + + /** + * 购买装扮 + */ + @GET("/dress/shop/optDress") + fun postOptDress(@Query("dressType") dressType: Int, @Query("dressId") id: Int): Single> + + /** + * 使用座驾 + * + * @param ticket 用户的ticket + * @param uid 用户的uid + * @param carId 座驾的id,id=0 则表示不使用座驾,id!=0 表示使用该carId的座驾 + */ + @POST("/car/carport/use") + @FormUrlEncoded + fun driveThisCar(@Field("ticket") ticket: String?, @Field("uid") uid: Long, @Field("carId") carId: Int): Single> + + /** + * 获取自己的车库V2 + * + * @param uid - + * @return - + */ + @GET("car/carport/v2/list") + fun requestMyCarsV2(@Query("uid") uid: Long): Single>> + + /** + * 使用头饰 + */ + @POST("v1/user/headwear/use") + fun userMyHeadWear(@Query("ticket") ticket: String?,@Query("uid") uid: String?, @Query("headwearId") headwear_id: String?): Single> + + /** + * 获取铭牌列表 + */ + @POST("nameplate/userNameplateList") + fun getNamePlateList(@Query("uid") uid: String?, @Query("page") page: String?, @Query("pageSize") pageSize: String?): Single?> + + @POST("nameplate/useNameplate") + fun useMyNamePlate(@Query("uid") uid: String?, @Query("ticket") ticket: String?, @Query("userNameplateId") id: String?, @Query("word") word: String?): Single> + + + /** + * 分页获取用户资料卡装扮列表 + */ + @GET("userInfoCard/listByPage") + fun getMyUserCardWearList(@Query("page") page: Int, @Query("pageSize") pageSize: Int): Single>> + + /** + * 使用资料卡装扮 + */ + @GET("userInfoCard/optUserInfoCard") + fun userMyUserCardWear(@Query("cardId") cardId: String?): Single> + + + /** + * 分页获取用户聊天气泡列表 + */ + @GET("userChatBubble/listByPage") + fun getChatBubbleInfoList(@Query("page") page: Int, @Query("pageSize") pageSize: Int): Single>> + + /** + * 使用聊天气泡 + */ + @FormUrlEncoded + @POST("userChatBubble/optUserChatBubble") + fun userChatBubble(@Field("bubbleId") cardId: String?): Single> + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/decoration/ui/fragment/DressUpStoreFragment.kt b/app/src/main/java/com/chwl/app/decoration/ui/fragment/DressUpStoreFragment.kt new file mode 100644 index 000000000..cbfeb3d8c --- /dev/null +++ b/app/src/main/java/com/chwl/app/decoration/ui/fragment/DressUpStoreFragment.kt @@ -0,0 +1,170 @@ +package com.chwl.app.decoration.ui.fragment + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.chwl.app.R +import com.chwl.app.base.BaseListFragment +import com.chwl.app.bindadapter.BaseBindingAdapter +import com.chwl.app.common.EmptyViewHelper +import com.chwl.app.decoration.adapter.DressUpAdapter +import com.chwl.app.decoration.util.DressUpUtil +import com.chwl.app.event.DressUpEvent +import com.chwl.core.bean.response.ServiceResult +import com.chwl.core.decoration.bean.DecorationInfo +import com.chwl.core.decoration.bean.DressUpInfo +import com.chwl.core.utils.net.RxHelper +import com.chwl.library.common.util.doToast +import com.chwl.library.common.util.setMargin +import com.chwl.library.net.rxnet.RxNet +import com.example.lib_utils.ktx.getString +import io.reactivex.Single +import org.greenrobot.eventbus.EventBus +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Query + +class DressUpStoreFragment: BaseListFragment() { + + private var mPosition = -1 + + companion object { + fun newInstance(dressType: Int): DressUpStoreFragment { + val args = Bundle() + args.putInt("dressType", dressType) + val fragment = DressUpStoreFragment() + fragment.arguments = args + return fragment + } + } + + private val mDressType: Int by lazy { requireArguments().getInt("dressType") } + + override fun initView() { + + binding.srlLayout.isEnableRefresh = false + binding.srlLayout.isEnableLoadmore = false + + mAdapter.setEmptyView(EmptyViewHelper.createEmptyTextViewHeight(context, R.string.empty_data.getString())) + binding.rvList.setMargin(start = 10, end = 10) + + } + + override fun onResume() { + super.onResume() + mPosition = -1 + onLoad() + } + + override fun getAdapter(): BaseBindingAdapter<*, DressUpInfo> { + return DressUpAdapter() + } + + override fun getLayoutManager(): RecyclerView.LayoutManager { + return GridLayoutManager(context,2,RecyclerView.VERTICAL,false) + } + + + override fun onLoad() { + getShopList() + .compose(bindToLifecycle()) + .doOnSuccess { + it?.let { + val newData = mutableListOf() + it.forEach { + newData.add(DressUpUtil.tranData(it)) + } + mAdapter.setNewData(newData) + } + } + .doOnError { + it?.message?.doToast() + } + .subscribe() + } + + override fun onRefresh() { + + } + + override fun onItemChildClicks(adapter: BaseBindingAdapter<*, DressUpInfo>?, view: View?, position: Int) { + view?.id?.let { + when (it) { + R.id.btnPlay -> { + adapter?.data?.getOrNull(position)?.let { data-> + EventBus.getDefault().post(DressUpEvent(DressUpEvent.Action.Play,data)) + } + } + else -> {} + } + } + } + + override fun onItemClicks(adapter: BaseBindingAdapter<*, DressUpInfo>?, view: View?, position: Int) { + adapter?.data?.getOrNull(position)?.let { data -> + + if (mPosition != -1) { + adapter?.data?.getOrNull(mPosition)?.isSelect = false + adapter?.notifyItemChanged(mPosition) + } + + mPosition = position + adapter?.data?.getOrNull(mPosition)?.isSelect = true + adapter?.notifyItemChanged(mPosition) + + EventBus.getDefault().post(DressUpEvent(DressUpEvent.Action.Select, data)) + } + } + + + + private fun getShopList(): Single> { + return api.getShopList(mDressType) + .compose(RxHelper.handleBeanData()) + .compose(RxHelper.handleSchedulers()) + } + + private val api: Api = RxNet.create(Api::class.java); + interface Api { + /** + * 获取装扮商城列表 + * + * @return + */ + @GET("/dress/shop/list") + fun getShopList(@Query("dressType") dressType: Int): Single>> + + /** + * 购买装扮 + * + * @return + */ + @FormUrlEncoded + @POST("/dress/shop/buy") + fun postShopBuy( + @Field("dressType") dressType: Int, + @Field("id") id: Int + ): Single> + + /** + * 赠送装扮 + * + * @return + */ + @FormUrlEncoded + @POST("/dress/shop/give") + fun postShopGive( + @Field("dressType") dressType: Int, + @Field("id") id: Int, + @Field("targetUid") targetUid: Long, + ): Single> + + } + + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/decoration/util/DressUpUtil.kt b/app/src/main/java/com/chwl/app/decoration/util/DressUpUtil.kt new file mode 100644 index 000000000..3a9523c42 --- /dev/null +++ b/app/src/main/java/com/chwl/app/decoration/util/DressUpUtil.kt @@ -0,0 +1,260 @@ +package com.chwl.app.decoration.util + +import android.graphics.Typeface +import android.view.ViewGroup +import android.widget.ImageView +import com.chwl.app.R +import com.chwl.app.avroom.widget.GalleryLayoutManager.LayoutParams +import com.chwl.app.decoration.ui.activity.DressUpTabActivity +import com.chwl.app.decoration.ui.activity.DressUpTabActivity.Companion.TAB_HEAD_WEAR +import com.chwl.app.decoration.ui.activity.DressUpTabActivity.Companion.TAB_USER_CARD +import com.chwl.app.ui.utils.loadAnim2 +import com.chwl.app.ui.utils.loadImage +import com.chwl.core.decoration.bean.DecorationInfo +import com.chwl.core.decoration.bean.DressUpInfo +import com.chwl.core.decoration.bean.ShopMine +import com.chwl.core.decoration.car.bean.CarInfo +import com.chwl.core.decoration.headwear.bean.ChatBubbleInfo +import com.chwl.core.decoration.headwear.bean.UserCardWearInfo +import com.chwl.core.decoration.nameplate.bean.NamePlateInfo +import com.chwl.library.common.util.isVerify +import com.chwl.library.common.util.setViewWH +import com.chwl.library.common.util.setVis +import com.chwl.library.widget.SVGAView +import com.chwl.library.widget.text.DrawableTextView +import com.example.lib_utils.ktx.getColor +import com.example.lib_utils.ktx.getString +import com.example.lib_utils.spannable.SpannableTextBuilder +import com.tencent.qgame.animplayer.AnimView +import com.tencent.qgame.animplayer.util.ScaleType + +class DressUpUtil { + companion object { + + fun getEmpty(use:Boolean): DressUpInfo { + val outData = DressUpInfo() + outData.dressId = 0 + outData.isMy = true + outData.isNull = true + outData.isSelect = use + return outData + } + + fun tranData(inData: DecorationInfo): DressUpInfo { + + val outData = DressUpInfo() + + outData.dressLimitStatus = inData.dressLimitStatus + outData.id = inData.id + outData.discount = inData.discount + outData.originalPrice = inData.dressPrice + outData.buyDay = inData.dressDay + outData.price = inData.discountPrice.toInt() + outData.name = inData.name + outData.pic = inData.pic + outData.effect = inData.effect + outData.effectType = inData.effectType + outData.dressType = inData.dressType + outData.vipLimit = inData.vipLimit + outData.dressId = inData.dressId + outData.obtainWay = inData.obtainWay + + return outData + } + + // 个人主页 , 头饰 + fun tranData(inData: ShopMine): DressUpInfo { + + val outData = DressUpInfo() + outData.isMy = true + outData.isSelect = inData.isUsed + outData.name = inData.name + outData.pic = inData.pic + outData.effect = inData.effect + outData.effectType = inData.effectType + outData.dressType = inData.dressType + outData.dressId = inData.dressId + outData.obtainWay = inData.obtainWay + + outData.hasExpired = inData.hasExpired + outData.expireDays = inData.expireDays + + return outData + } + + fun tranData(inData: ChatBubbleInfo): DressUpInfo { + + val outData = DressUpInfo() + outData.isMy = true + outData.isSelect = inData.isHasUsed + outData.name = inData.name + outData.pic = inData.bubbleUrl + outData.dressType = DressUpTabActivity.TAB_CHAT_BUBBLE + if (inData.bubbleId.isVerify()) { + outData.dressId = inData.bubbleId.toInt() + } + outData.hasExpired = inData.isHasExpired + outData.expireDays = inData.expireDays + + return outData + } + + fun tranData(inData: UserCardWearInfo): DressUpInfo { + + val outData = DressUpInfo() + outData.isMy = true + outData.isSelect = inData.isUsed + outData.name = inData.name + outData.pic = inData.pic + outData.effect = inData.pic + outData.effectType = DressUpInfo.EffectType.MP4 + outData.dressType = DressUpTabActivity.TAB_USER_CARD + if (inData.cardId.isVerify()) { + outData.dressId = inData.cardId.toInt() + } + + outData.hasExpired = inData.isHasExpired + outData.expireDays = inData.expireDays + + return outData + } + + fun tranData(inData: NamePlateInfo.NameplateListBean): DressUpInfo { + + val outData = DressUpInfo() + outData.isMy = true + outData.isSelect = inData.isUsing + outData.name = inData.nameplateName + outData.word = inData.word + outData.pic = inData.nameplateImage + outData.dressType = DressUpTabActivity.TAB_NAMEPLATE + outData.dressId = inData.nameplateId + outData.id = inData.id + + outData.hasExpired = inData.isExpired + outData.expireDays = inData.expireDays + + return outData + } + + fun tranData(inData: CarInfo): DressUpInfo { + + val outData = DressUpInfo() + outData.isMy = true + outData.isSelect = inData.using == 1 + outData.name = inData.name + outData.pic = inData.pic + if (inData.viewUrl.isVerify()) { + outData.effect = inData.viewUrl + outData.effectType = inData.otherViewType + }else{ + outData.effect = inData.effect + outData.effectType = inData.otherViewType + } + outData.dressType = DressUpTabActivity.TAB_CAR + outData.dressId = inData.carId + + outData.hasExpired = inData.status != CarInfo.STATUS_USER_CAN_USE + +// val cTime = System.currentTimeMillis() +// val dTime = inData.expireDays - cTime +// val dDay = TimeUnit.MICROSECONDS.toDays(dTime) +// if (dDay < TimeUnit.DAYS.toMillis(1)) { +// // < 1 天 +// } else { +// // dDay天后过期 +// } + + outData.expireDays = inData.days + + return outData + } + + + + fun setTextPrice(textView: DrawableTextView, item: DressUpInfo) { + val message = R.string.s_sDays_s.getString(item.price, item.buyDay, item.originalPrice) + SpannableTextBuilder(textView).appendText(message) + .setTextStyle( + text = item.price.toString(), + textStyle = Typeface.BOLD + ).setTextStyle( + text = item.buyDay.toString(), + textStyle = Typeface.NORMAL + ).setTextStyle( + text = item.originalPrice.toString(), + textColor = R.color.color_d9e7f7.getColor(), + delLine = true + ).apply() + textView.setVis(true) + } + + fun setItemPic(data:DressUpInfo,layout: ViewGroup) { + if (data.dressType == DressUpTabActivity.TAB_USER_CARD) { + //此时传回的是mp4.. +// layout.setViewWH() + play(data,layout,true) + } else { + layout.removeAllViews() + val pic = ImageView(layout.context) + layout.addView(pic) + if (data.dressType == DressUpTabActivity.TAB_CAR) { + pic.setViewWH(129,129) + }else if (data.dressType == DressUpTabActivity.TAB_HEAD_WEAR) { + pic.setViewWH(117,117) + }else if (data.dressType == DressUpTabActivity.TAB_NAMEPLATE) { + pic.setViewWH(98,28) + }else if (data.dressType == DressUpTabActivity.TAB_CHAT_BUBBLE) { + pic.setViewWH(90,46) + }else if (data.dressType == DressUpTabActivity.TAB_USER_INFO_BG) { + pic.setViewWH(127,125) + } + pic.loadImage(data.pic) + } + } + + fun play(data:DressUpInfo,layout: ViewGroup,isItem :Boolean = false){ + if (data.effect.isVerify()) { + + var animView : AnimView?=null + var svgaView : SVGAView?=null + + layout.setVis(true) + layout.removeAllViews() + + if (data.effectType == DressUpInfo.EffectType.MP4) { + animView = AnimView(layout.context) + layout.addView(animView) + animView.setLoop(Int.MAX_VALUE) + animView.setScaleType(ScaleType.FIT_XY) + } else { + svgaView = SVGAView(layout.context) + layout.addView(svgaView) + svgaView.loops = Int.MAX_VALUE + svgaView.setScaleType(ImageView.ScaleType.FIT_XY) + } + + //item 时占满就行 + if (isItem) { + animView?.setViewWH(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, false) + svgaView?.setViewWH(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,false) + } else { + if (data.dressType == TAB_HEAD_WEAR) { + animView?.setViewWH(183, 183, true) + svgaView?.setViewWH(183, 183, true) + }else if (data.dressType == TAB_USER_CARD) { + animView?.setViewWH(285, 250, true) + svgaView?.setViewWH(285, 250, true) + } else { + animView?.setViewWH(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, false) + svgaView?.setViewWH(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,false) + } + } + + + animView?.loadAnim2(data.effect,false) + svgaView?.loadUrl(data.effect,true) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/decoration/view/DecorationStoreActivity.kt b/app/src/main/java/com/chwl/app/decoration/view/DecorationStoreActivity.kt index f119003e4..9a339d5cf 100644 --- a/app/src/main/java/com/chwl/app/decoration/view/DecorationStoreActivity.kt +++ b/app/src/main/java/com/chwl/app/decoration/view/DecorationStoreActivity.kt @@ -11,6 +11,7 @@ import com.chwl.app.R import com.chwl.app.base.BaseViewBindingActivity import com.chwl.app.base.TitleBar import com.chwl.app.databinding.ActivityDecorationStoreBinding +import com.chwl.app.decoration.ui.activity.DressUpTabActivity import com.chwl.app.decoration.view.widgets.CarMagicIndicator import com.chwl.app.decoration.view.widgets.MyDecorationMagicIndicator import com.chwl.app.decoration.viewmodel.DecorationViewModel @@ -60,7 +61,7 @@ class DecorationStoreActivity : BaseViewBindingActivity { //todo do 测试按钮 if (BuildConfig.DEBUG) { - start(requireActivity(), 8) + DressUpTabActivity.start(requireActivity(),false) } } diff --git a/app/src/main/java/com/chwl/app/support/PreloadResourceViewModel.kt b/app/src/main/java/com/chwl/app/support/PreloadResourceViewModel.kt index 0dc8ac741..ebd8406ed 100644 --- a/app/src/main/java/com/chwl/app/support/PreloadResourceViewModel.kt +++ b/app/src/main/java/com/chwl/app/support/PreloadResourceViewModel.kt @@ -7,17 +7,18 @@ import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target -import com.chwl.app.BuildConfig import com.chwl.app.base.BaseViewModel -import com.chwl.library.utils.PathHelper import com.chwl.core.home.model.HomeModel +import com.chwl.core.utils.LogUtils import com.chwl.library.common.glide.GlideUtils import com.chwl.library.common.util.SPUtils import com.chwl.library.utils.NetworkUtils +import com.chwl.library.utils.PathHelper import com.example.lib_utils.AppUtils import com.example.lib_utils.FileUtils2 import com.example.lib_utils.log.ILog import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.io.File @@ -33,18 +34,24 @@ class PreloadResourceViewModel : BaseViewModel(), ILog , RequestListener private var mContext : Context? = null + //todo 预加载 fun start(context: Context) { mContext = context - if (BuildConfig.DEBUG) { - // 太多请求了,影响查看控制台日志 - return - } + +// if (BuildConfig.DEBUG) { +// // 太多请求了,影响查看控制台日志 +// return +// } + if (isStarted) { return } - getPreloadResourceList { - isStarted = true - nextTask(null) + + viewModelScope?.launch(Dispatchers.IO) { + getPreloadResourceList { + isStarted = true + nextTask(null) + } } } @@ -91,7 +98,7 @@ class PreloadResourceViewModel : BaseViewModel(), ILog , RequestListener } } - private fun downloadTask(url: String) { + private fun downloadTask(url: String?) { // val path = PathHelper.generateResourcesFilePath(url) // logI("downloadTask() url:${url} path:${path}") @@ -104,9 +111,13 @@ class PreloadResourceViewModel : BaseViewModel(), ILog , RequestListener // ) // DownloadManager.download(request, this) - GlideUtils.instance().downloadFromUrl(mContext,url,this) - - + viewModelScope.launch { + delay(500) + LogUtils.d("预加载 -> downloadTask url = ${url?.trim()}") + if (url != null) { + GlideUtils.instance().downloadFromUrl2(mContext,url.trim(),this@PreloadResourceViewModel) + } + } } override fun onLoadFailed( @@ -119,6 +130,7 @@ class PreloadResourceViewModel : BaseViewModel(), ILog , RequestListener logI("onDownloadError() url:${model} message:${e?.message}") nextTask(model) } + LogUtils.d("预加载 onLoadFailed 失败 url = ${model}") return true } @@ -134,7 +146,7 @@ class PreloadResourceViewModel : BaseViewModel(), ILog , RequestListener makeDownloadRecord(model) nextTask(null) } - + LogUtils.d("预加载 onResourceReady 成功 url = ${model}") return true } diff --git a/app/src/main/java/com/chwl/app/ui/im/RouterHandler.java b/app/src/main/java/com/chwl/app/ui/im/RouterHandler.java index d0ab0cbee..59892014d 100644 --- a/app/src/main/java/com/chwl/app/ui/im/RouterHandler.java +++ b/app/src/main/java/com/chwl/app/ui/im/RouterHandler.java @@ -4,16 +4,6 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import com.chwl.app.decoration.view.DecorationStoreActivity; -import com.chwl.app.earn.activity.EarnRecordActivity; -import com.chwl.app.pay.activity.GiveGoldActivity; -import com.chwl.app.ui.feedback.FeedbackActivity; -import com.chwl.app.ui.game_team.record.GameTeamRecordActivity; -import com.chwl.app.ui.setting.VipSetActivity; -import com.chwl.app.vip.VipCenterActivity; -import com.chwl.core.module_hall.hall.bean.H5FamilyInfo; -import com.chwl.core.module_hall.hall.bean.UserClanInfo; -import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; import com.chwl.app.R; import com.chwl.app.UIHelper; import com.chwl.app.audio.VoiceMatchActivity; @@ -22,31 +12,38 @@ import com.chwl.app.avroom.recommendcard.MyRecommendCardActivity; import com.chwl.app.base.BaseActivity; import com.chwl.app.base.DialogManagerInterface; import com.chwl.app.common.widget.dialog.DialogManager; -import com.chwl.app.decoration.view.MyDecorationActivity; +import com.chwl.app.decoration.ui.activity.DressUpTabActivity; +import com.chwl.app.earn.activity.EarnRecordActivity; import com.chwl.app.home.activity.CollectionRoomActivity; import com.chwl.app.home.activity.VisitorListActivity; import com.chwl.app.home.helper.OpenRoomHelper; import com.chwl.app.module_hall.HallDataManager; import com.chwl.app.module_hall.hall.activity.ModuleClanActivity; import com.chwl.app.module_hall.hall.activity.ModuleHallActivity; +import com.chwl.app.pay.activity.GiveGoldActivity; import com.chwl.app.radish.task.activity.TaskCenterActivity; import com.chwl.app.relation.cp.activity.CpHomeActivity; import com.chwl.app.relation.cp.activity.CpInviteRecordActivity; import com.chwl.app.relation.cp.activity.CpTaskActivity; import com.chwl.app.team.view.NimTeamMessageActivity; +import com.chwl.app.ui.feedback.FeedbackActivity; +import com.chwl.app.ui.game_team.record.GameTeamRecordActivity; import com.chwl.app.ui.login.BindPhoneActivity; import com.chwl.app.ui.login.ShowBindPhoneActivity; import com.chwl.app.ui.pay.ChargeActivity; import com.chwl.app.ui.setting.ModifyPwdActivity; import com.chwl.app.ui.user.activity.UserInfoActivity; import com.chwl.app.ui.webview.CommonWebViewActivity; +import com.chwl.app.vip.VipCenterActivity; import com.chwl.core.DemoCache; import com.chwl.core.auth.AuthModel; import com.chwl.core.community.event.SquareTaskEvent; import com.chwl.core.im.custom.bean.RouterType; import com.chwl.core.manager.AvRoomDataManager; import com.chwl.core.module_hall.hall.HallModel; +import com.chwl.core.module_hall.hall.bean.H5FamilyInfo; import com.chwl.core.module_hall.hall.bean.HallInfo; +import com.chwl.core.module_hall.hall.bean.UserClanInfo; import com.chwl.core.room.bean.RoomInfo; import com.chwl.core.room.model.AvRoomModel; import com.chwl.core.utils.net.BeanObserver; @@ -55,6 +52,7 @@ import com.chwl.library.net.rxnet.callback.CallBack; import com.chwl.library.utils.JavaUtil; import com.chwl.library.utils.ResUtil; import com.chwl.library.utils.SingleToastUtil; +import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; import org.greenrobot.eventbus.EventBus; @@ -104,27 +102,27 @@ public class RouterHandler { break; case RouterType.CAR: if (JavaUtil.str2int(routerValue) == 0) { - DecorationStoreActivity.start(context, DecorationStoreActivity.TAB_CAR); + DressUpTabActivity.Companion.start(context,false); } else { - MyDecorationActivity.start(context, 1); + DressUpTabActivity.Companion.start(context,true); } break; case RouterType.DECORATION: if (JavaUtil.str2int(routerValue) == 0) { - DecorationStoreActivity.start(context, 0); + DressUpTabActivity.Companion.start(context,false); } else { - MyDecorationActivity.start(context, 0); + DressUpTabActivity.Companion.start(context,true); } break; case RouterType.NAMEPLATE: if (JavaUtil.str2int(routerValue) == 0) { - DecorationStoreActivity.start(context, DecorationStoreActivity.TAB_NAMEPLATE); + DressUpTabActivity.Companion.start(context,false); } else { - MyDecorationActivity.start(context, 2); + DressUpTabActivity.Companion.start(context,true); } break; case RouterType.USER_CARD_WEAR: - MyDecorationActivity.start(context, 3); + DressUpTabActivity.Companion.start(context,true); break; case RouterType.TEAM: NimTeamMessageActivity.start(context, routerValue); @@ -226,10 +224,10 @@ public class RouterHandler { CpTaskActivity.Companion.start(context); break; case RouterType.CP_WARE: - MyDecorationActivity.start(context, 0); + DressUpTabActivity.Companion.start(context,true); break; case RouterType.USER_BUBBLE: - MyDecorationActivity.start(context, 4); + DressUpTabActivity.Companion.start(context,true); break; case RouterType.MY_ROOM: //我的房间,开房 @@ -278,13 +276,13 @@ public class RouterHandler { if (intRouterValue < 0 || intRouterValue > 4) { intRouterValue = 0; } - DecorationStoreActivity.start(context, intRouterValue); + DressUpTabActivity.Companion.start(context,false); break; case RouterType.MY_DECORATION: if (intRouterValue < 0 || intRouterValue > 4) { intRouterValue = 0; } - MyDecorationActivity.start(context, intRouterValue); + DressUpTabActivity.Companion.start(context,true); break; case RouterType.MY_SET: UIHelper.showSettingAct(context); @@ -299,7 +297,7 @@ public class RouterHandler { GameTeamRecordActivity.Companion.start(context); break; case RouterType.MY_DRESS_ITEM: - MyDecorationActivity.start(context, 0); + DressUpTabActivity.Companion.start(context,true); break; default: SingleToastUtil.showToast(ResUtil.getString(R.string.ui_im_routerhandler_07)); diff --git a/app/src/main/java/com/chwl/app/ui/im/chat/MsgViewHolderText.java b/app/src/main/java/com/chwl/app/ui/im/chat/MsgViewHolderText.java index 76df73acd..ee98c2cad 100644 --- a/app/src/main/java/com/chwl/app/ui/im/chat/MsgViewHolderText.java +++ b/app/src/main/java/com/chwl/app/ui/im/chat/MsgViewHolderText.java @@ -7,11 +7,8 @@ import android.text.style.ForegroundColorSpan; import android.view.View; import android.widget.TextView; -import com.netease.nim.uikit.business.session.viewholder.MsgViewHolderBase; -import com.netease.nim.uikit.common.ui.recyclerview.adapter.BaseMultiItemFetchLoadAdapter; -import com.netease.nimlib.sdk.msg.attachment.MsgAttachment; import com.chwl.app.R; -import com.chwl.app.decoration.view.MyDecorationActivity; +import com.chwl.app.decoration.ui.activity.DressUpTabActivity; import com.chwl.app.ui.im.RouterHandler; import com.chwl.app.ui.webview.CommonWebViewActivity; import com.chwl.app.utils.SpannableBuilder; @@ -28,6 +25,9 @@ import com.chwl.core.im.custom.bean.NobleAttachment; import com.chwl.core.im.custom.bean.VoiceBottleShakeHeartAttachment; import com.chwl.core.manager.IMNetEaseManager; import com.chwl.library.utils.ResUtil; +import com.netease.nim.uikit.business.session.viewholder.MsgViewHolderBase; +import com.netease.nim.uikit.common.ui.recyclerview.adapter.BaseMultiItemFetchLoadAdapter; +import com.netease.nimlib.sdk.msg.attachment.MsgAttachment; import org.greenrobot.eventbus.EventBus; @@ -151,7 +151,7 @@ public class MsgViewHolderText extends MsgViewHolderBase { } else if (mCarAttachment != null) { if (mCarAttachment.mSecond == CUSTOM_MESS_SUB_CAR_EXPIRE) { - MyDecorationActivity.start(mTvMsg.getContext(), 1); + DressUpTabActivity.Companion.start(context,true); } } else if (awardAttachment != null) { RouterHandler.handle(context, awardAttachment.routerType, awardAttachment.routerValue); diff --git a/app/src/main/java/com/chwl/app/ui/search/SearchUserActivity.kt b/app/src/main/java/com/chwl/app/ui/search/SearchUserActivity.kt new file mode 100644 index 000000000..b436e7c6b --- /dev/null +++ b/app/src/main/java/com/chwl/app/ui/search/SearchUserActivity.kt @@ -0,0 +1,158 @@ +package com.chwl.app.ui.search + +import android.content.Context +import android.content.Intent +import androidx.core.widget.doOnTextChanged +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.chwl.app.R +import com.chwl.app.base.BaseViewBindingActivity +import com.chwl.app.bindadapter.BaseBindingAdapter +import com.chwl.app.bindadapter.BaseBindingViewHolder +import com.chwl.app.common.EmptyViewHelper +import com.chwl.app.databinding.ActivitySearchUserBinding +import com.chwl.app.databinding.ItemSearchUserBinding +import com.chwl.app.decoration.ui.DressUpDialog +import com.chwl.app.ui.utils.loadAvatar +import com.chwl.core.bean.response.ServiceResult +import com.chwl.core.decoration.bean.DressUpInfo +import com.chwl.core.im.friend.IMFriendModel +import com.chwl.core.room.bean.SearchRoomInfo +import com.chwl.core.user.UserModel +import com.chwl.core.user.bean.UserInfo +import com.chwl.core.utils.net.RxHelper +import com.chwl.library.common.util.doToast +import com.chwl.library.common.util.isVerify +import com.chwl.library.net.rxnet.RxNet +import com.example.lib_utils.ktx.getString +import com.google.gson.Gson +import io.reactivex.Single +import retrofit2.http.GET +import retrofit2.http.Query + +class SearchUserActivity : BaseViewBindingActivity() { + + companion object{ + + fun start(context: Context, data:String) { + val intent = Intent(context, SearchUserActivity::class.java) + intent.putExtra("json",data) + context.startActivity(intent) + } + } + + private var mDressUpInfo : DressUpInfo? = null + private val mJson : String by lazy { intent.getStringExtra("json")?:"" } + lateinit var mAdapter : UserAdapter + override fun transparencyBar() = true + + + override fun init() { + initDarkTitleBar(R.string.search.getString()) + + if (mJson.isVerify()) { + mDressUpInfo = Gson().fromJson(mJson,DressUpInfo::class.java) + } + + mAdapter= UserAdapter() + binding.rvList.layoutManager = LinearLayoutManager(context,RecyclerView.VERTICAL,false) + binding.rvList.adapter = mAdapter + mAdapter.setEmptyView(EmptyViewHelper.createEmptyTextViewHeight(context,R.string.no_frenids_text.getString())) + + mAdapter.setOnItemChildClickListener { adapter, view, position -> + + when (view.id) { + R.id.btnGive -> { + val uid = mAdapter?.data?.getOrNull(position)?.uid + if (uid != null && mDressUpInfo != null) { + val dressUpDialog = DressUpDialog() + dressUpDialog.mDressUpInfo = mDressUpInfo + dressUpDialog.mTargetUid = uid + dressUpDialog.show(context) + } + } + else -> {} + } + } + + binding.etSearch.doOnTextChanged { text, start, before, count -> + text?.let { + doSearch(text.toString()) + } + } + + loadFriends() + + } + + private fun loadFriends() { + val accounts = IMFriendModel.get().myFriendsAccounts + if (accounts.isVerify()) { + UserModel.get().loadUserInfoByUids(accounts) + .compose(bindToLifecycle()) + .doOnSuccess { + mAdapter.setNewData(it) + } + .doOnError { + it?.message?.doToast() + }.subscribe() + } + } + + private fun doSearch(key: String) { + roomSearch(key) + .compose(bindToLifecycle()) + .doOnSuccess { + if (it.isVerify()) { + val newData = mutableListOf() + it.forEach { + newData.add(UserInfo().apply { + avatar = it.avatar + uid = it.uid + erbanNo = it.erbanNo + nick = it.nick + }) + mAdapter.setNewData(newData) + } + } + }.doOnError { + it?.message?.doToast() + }.subscribe() + } + + + + + class UserAdapter : BaseBindingAdapter() { + + override fun convert(helper: BaseBindingViewHolder, item: UserInfo) { + helper.binding.let { + it.avatar.loadAvatar(item.avatar) + it.name.text = item.nick + it.id.text = item.erbanNo.toString() + + helper.addOnClickListener(it.btnGive.id) + } + } + } + + private fun roomSearch(key: String) : Single>{ + return api.roomSearch(key,2) + .compose(RxHelper.handleBeanData()) + .compose(RxHelper.handleSchedulers()) + } + + private val api: Api = RxNet.create(Api::class.java); + + interface Api { + @GET("/search/room") + fun roomSearch( + @Query("key") key: String?, + @Query("type") type: Int + ): Single>> + } + + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/chwl/app/ui/user/activity/UserCpListActivity.kt b/app/src/main/java/com/chwl/app/ui/user/activity/UserCpListActivity.kt index 7679421a3..4a5859c2e 100644 --- a/app/src/main/java/com/chwl/app/ui/user/activity/UserCpListActivity.kt +++ b/app/src/main/java/com/chwl/app/ui/user/activity/UserCpListActivity.kt @@ -3,7 +3,6 @@ package com.chwl.app.ui.user.activity import android.content.Context import android.content.Intent import android.view.View -import androidx.core.content.ContentProviderCompat.requireContext import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -21,18 +20,13 @@ import com.chwl.core.UriProvider import com.chwl.core.bean.response.ServiceResult import com.chwl.core.user.bean.UserCPListBean import com.chwl.core.utils.LogUtils -import com.chwl.core.utils.net.DontWarnObserver import com.chwl.core.utils.net.RxHelper import com.chwl.library.net.rxnet.RxNet import com.chwl.library.utils.ResUtil import com.example.lib_utils.ktx.getString import com.google.gson.JsonElement -import com.hjq.toast.ToastUtils import com.netease.nim.uikit.StatusBarUtil -import com.trello.rxlifecycle3.android.ActivityEvent import io.reactivex.Single -import io.reactivex.SingleObserver -import io.reactivex.disposables.Disposable import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Query @@ -59,7 +53,7 @@ class UserCpListActivity : BaseViewBindingActivity() } private fun initTitle() { - initBlackTitleBar(ResUtil.getString(R.string.My_Cp)) + initDarkTitleBar(ResUtil.getString(R.string.My_Cp)) val titleBar = findViewById(R.id.title_bar) as TitleBar titleBar.addAction(object : ImageAction(R.drawable.ic_cp_list_more) { override fun performAction(view: View?) { diff --git a/app/src/main/java/com/chwl/app/ui/utils/ImageLoad.kt b/app/src/main/java/com/chwl/app/ui/utils/ImageLoad.kt index 9c5e1814b..442f42199 100644 --- a/app/src/main/java/com/chwl/app/ui/utils/ImageLoad.kt +++ b/app/src/main/java/com/chwl/app/ui/utils/ImageLoad.kt @@ -173,9 +173,9 @@ fun AnimView.loadAnim(url: String) { }) } -fun AnimView.loadAnim2(url: String) { +fun AnimView.loadAnim2(url: String,isSafe:Boolean = true) { if (context.isDestroyed()) return - GlideUtils.instance().downloadFromUrl(context,url.trim(),object : RequestListener { + GlideUtils.instance().downloadFromUrl2(context,url.trim(),object : RequestListener { override fun onLoadFailed( e: GlideException?, model: Any?, @@ -193,9 +193,14 @@ fun AnimView.loadAnim2(url: String) { isFirstResource: Boolean ): Boolean { if (resource != null) { - if (this@loadAnim2.isAttachedToWindow){ + if (isSafe) { + if (this@loadAnim2.isAttachedToWindow){ + this@loadAnim2.startPlay(resource) + } + } else { this@loadAnim2.startPlay(resource) } + } return true } diff --git a/app/src/main/java/com/chwl/app/ui/webview/JSInterface.java b/app/src/main/java/com/chwl/app/ui/webview/JSInterface.java index 74d271319..9f189354b 100644 --- a/app/src/main/java/com/chwl/app/ui/webview/JSInterface.java +++ b/app/src/main/java/com/chwl/app/ui/webview/JSInterface.java @@ -5,9 +5,7 @@ import static android.content.Context.CLIPBOARD_SERVICE; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.media.MediaRecorder; import android.os.Build; -import android.os.Environment; import android.os.Looper; import android.text.TextUtils; import android.util.Log; @@ -18,34 +16,27 @@ import androidx.annotation.NonNull; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.chwl.app.earn.activity.EarnRecordActivity; -import com.chwl.app.ui.invite.InviteImageHelper; -import com.chwl.app.ui.invite.ShareInviteDialog; -import com.chwl.app.ui.invite.ShareInviteInfo; -import com.chwl.app.ui.wallet.payment.GPaymentClient; -import com.chwl.app.ui.wallet.payment.IPaymentClient; -import com.chwl.app.ui.wallet.payment.PaymentIntent; -import com.chwl.app.ui.wallet.payment.PaymentResult; -import com.chwl.library.language.LanguageHelper; -import com.chwl.library.utils.json.JsonUtils; -import com.example.lib_utils.ICleared; -import com.google.gson.Gson; -import com.netease.nim.uikit.common.util.log.LogUtil; -import com.orhanobut.logger.Logger; import com.chwl.app.UIHelper; import com.chwl.app.application.IReportConstants; import com.chwl.app.application.ReportManager; import com.chwl.app.avroom.activity.AVRoomActivity; +import com.chwl.app.earn.activity.EarnRecordActivity; import com.chwl.app.ui.im.RouterHandler; import com.chwl.app.ui.im.avtivity.NimP2PMessageActivity; +import com.chwl.app.ui.invite.InviteImageHelper; +import com.chwl.app.ui.invite.ShareInviteDialog; +import com.chwl.app.ui.invite.ShareInviteInfo; import com.chwl.app.ui.pay.ChargeActivity; +import com.chwl.app.ui.wallet.payment.GPaymentClient; +import com.chwl.app.ui.wallet.payment.IPaymentClient; +import com.chwl.app.ui.wallet.payment.PaymentIntent; +import com.chwl.app.ui.wallet.payment.PaymentResult; import com.chwl.app.ui.webview.event.H5NotifyClientEvent; import com.chwl.app.ui.webview.event.ShowNavEvent; import com.chwl.app.ui.webview.event.TaroPayResultEvent; import com.chwl.core.XConstants; import com.chwl.core.auth.AuthModel; import com.chwl.core.decoration.bean.DecorationStoreRouterType; -import com.chwl.core.file.FileModel; import com.chwl.core.manager.AvRoomDataManager; import com.chwl.core.market_verify.MarketVerifyModel; import com.chwl.core.pay.PayModel; @@ -55,18 +46,21 @@ import com.chwl.core.room.bean.RoomInfo; import com.chwl.core.user.UserModel; import com.chwl.core.web.bean.WebJsBeanInfo; import com.chwl.library.common.util.DeviceUtil; -import com.chwl.library.record.AuditRecorderConfiguration; -import com.chwl.library.record.ExtAudioRecorder; +import com.chwl.library.language.LanguageHelper; import com.chwl.library.rxbus.RxBus; import com.chwl.library.utils.AppMetaDataUtil; import com.chwl.library.utils.DeviceUuidFactory; import com.chwl.library.utils.SystemUtils; import com.chwl.library.utils.VersionUtil; import com.chwl.library.utils.config.BasicConfig; +import com.chwl.library.utils.json.JsonUtils; +import com.example.lib_utils.ICleared; +import com.google.gson.Gson; +import com.netease.nim.uikit.common.util.log.LogUtil; +import com.orhanobut.logger.Logger; import org.greenrobot.eventbus.EventBus; -import java.io.File; import java.util.HashMap; /** @@ -79,9 +73,9 @@ public class JSInterface implements ICleared { private CommonWebViewActivity mActivity; private Context context; private int mPosition; - private MediaRecorder recorder; - private File myRecAudioFile; - private ExtAudioRecorder extAudioRecorder; +// private MediaRecorder recorder; +// private File myRecAudioFile; +// private ExtAudioRecorder extAudioRecorder; private GPaymentClient paymentClient; public JSInterface(WebView webView, CommonWebViewActivity activity) { @@ -420,45 +414,45 @@ public class JSInterface implements ICleared { NimP2PMessageActivity.start(context, uid); } - @JavascriptInterface - public boolean startRecode() { - // recorder 实例 - AuditRecorderConfiguration configuration = new AuditRecorderConfiguration.Builder() - .uncompressed(true) - .builder(); - extAudioRecorder = new ExtAudioRecorder(configuration); - // 录音地址 - File sdcardDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC).getAbsolutePath() + - File.separator + "wewawa"); - if (!sdcardDir.exists()) { - boolean mkdirResult = sdcardDir.mkdir(); - Log.e(TAG, "startRecode: mkdirResult: " + mkdirResult); - } - myRecAudioFile = new File(sdcardDir.getAbsolutePath(), "wewawa-" + System.currentTimeMillis() + ".wav"); - Log.i(TAG, "startRecode: myRecAudioFile path: " + myRecAudioFile.getAbsolutePath()); - // 设置输出文件 - extAudioRecorder.setOutputFile(myRecAudioFile.getAbsolutePath()); - extAudioRecorder.prepare(); - extAudioRecorder.start(); - return true; - } - - @JavascriptInterface - public String stopRecode() { - if (extAudioRecorder == null || myRecAudioFile == null) - return null; - if (!myRecAudioFile.exists()) { - return null; - } - extAudioRecorder.stop(); - extAudioRecorder.release(); - String url = FileModel.get() - .uploadFile(myRecAudioFile.getAbsolutePath()) - .blockingGet(); - myRecAudioFile.delete(); - Log.i(TAG, "stopRecord: url: " + url); - return url; - } +// @JavascriptInterface +// public boolean startRecode() { +// // recorder 实例 +// AuditRecorderConfiguration configuration = new AuditRecorderConfiguration.Builder() +// .uncompressed(true) +// .builder(); +// extAudioRecorder = new ExtAudioRecorder(configuration); +// // 录音地址 +// File sdcardDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC).getAbsolutePath() + +// File.separator + "wewawa"); +// if (!sdcardDir.exists()) { +// boolean mkdirResult = sdcardDir.mkdir(); +// Log.e(TAG, "startRecode: mkdirResult: " + mkdirResult); +// } +// myRecAudioFile = new File(sdcardDir.getAbsolutePath(), "wewawa-" + System.currentTimeMillis() + ".wav"); +// Log.i(TAG, "startRecode: myRecAudioFile path: " + myRecAudioFile.getAbsolutePath()); +// // 设置输出文件 +// extAudioRecorder.setOutputFile(myRecAudioFile.getAbsolutePath()); +// extAudioRecorder.prepare(); +// extAudioRecorder.start(); +// return true; +// } +// +// @JavascriptInterface +// public String stopRecode() { +// if (extAudioRecorder == null || myRecAudioFile == null) +// return null; +// if (!myRecAudioFile.exists()) { +// return null; +// } +// extAudioRecorder.stop(); +// extAudioRecorder.release(); +// String url = FileModel.get() +// .uploadFile(myRecAudioFile.getAbsolutePath()) +// .blockingGet(); +// myRecAudioFile.delete(); +// Log.i(TAG, "stopRecord: url: " + url); +// return url; +// } @JavascriptInterface public void openFamilyPage(String familyId) { diff --git a/app/src/main/java/com/chwl/app/ui/webview/baishun/BaiShunGameWebFragment.kt b/app/src/main/java/com/chwl/app/ui/webview/baishun/BaiShunGameWebFragment.kt index 7e6a3fddc..789f9f01d 100644 --- a/app/src/main/java/com/chwl/app/ui/webview/baishun/BaiShunGameWebFragment.kt +++ b/app/src/main/java/com/chwl/app/ui/webview/baishun/BaiShunGameWebFragment.kt @@ -81,7 +81,10 @@ class BaiShunGameWebFragment : BaseViewBindingFragment( super.onPageStarted(view, url, favicon) isLoadError = false if (url == gameUrl) { - dialogManager.showProgressDialog(requireContext()) + context?.let { + dialogManager.showProgressDialog(it) + } + } } @@ -162,7 +165,7 @@ class BaiShunGameWebFragment : BaseViewBindingFragment( settings?.domStorageEnabled = true //设置开启数据库缓存 settings?.databaseEnabled = true - settings?.databasePath = (requireContext().applicationContext.filesDir.absolutePath) + settings?.databasePath = (context?.applicationContext?.filesDir?.absolutePath) //设置⽀持缩放 settings?.setSupportZoom(true) //设置⾃适应 diff --git a/app/src/main/java/com/chwl/app/ui/webview/baishun/LeaderccGameWebFragment.kt b/app/src/main/java/com/chwl/app/ui/webview/baishun/LeaderccGameWebFragment.kt index c03377859..82766ffbb 100644 --- a/app/src/main/java/com/chwl/app/ui/webview/baishun/LeaderccGameWebFragment.kt +++ b/app/src/main/java/com/chwl/app/ui/webview/baishun/LeaderccGameWebFragment.kt @@ -86,7 +86,7 @@ class LeaderccGameWebFragment : BaseViewBindingFragment super.onPageStarted(view, url, favicon) isLoadError = false if (url == gameUrl) { - dialogManager.showProgressDialog(requireContext()) + dialogManager.showProgressDialog(context) } } @@ -162,7 +162,7 @@ class LeaderccGameWebFragment : BaseViewBindingFragment settings?.domStorageEnabled = true //设置开启数据库缓存 settings?.databaseEnabled = true - settings?.databasePath = (requireContext().applicationContext.filesDir.absolutePath) + settings?.databasePath = (context?.applicationContext?.filesDir?.absolutePath) //设置⽀持缩放 settings?.setSupportZoom(true) //设置⾃适应 diff --git a/app/src/main/java/com/chwl/app/ui/widget/UserInfoDialog.java b/app/src/main/java/com/chwl/app/ui/widget/UserInfoDialog.java index 02e7cac3b..de6d10b97 100644 --- a/app/src/main/java/com/chwl/app/ui/widget/UserInfoDialog.java +++ b/app/src/main/java/com/chwl/app/ui/widget/UserInfoDialog.java @@ -667,7 +667,7 @@ public class UserInfoDialog extends AppCompatDialog implements View.OnClickListe private void drawVAPEffect(String url) { - GlideUtils.instance().downloadFromUrl(getContext(), url, new RequestListener() { + GlideUtils.instance().downloadFromUrl2(getContext(), url, new RequestListener() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { ImageLoadUtils.loadImage(context, userInfo.getUserInfoCardPic(), ivUserCardWear); diff --git a/app/src/main/java/com/chwl/app/utils/RoomBoomManager.kt b/app/src/main/java/com/chwl/app/utils/RoomBoomManager.kt index 2fdbab8d8..33df2c631 100644 --- a/app/src/main/java/com/chwl/app/utils/RoomBoomManager.kt +++ b/app/src/main/java/com/chwl/app/utils/RoomBoomManager.kt @@ -115,7 +115,7 @@ object RoomBoomManager { urls.forEach { url -> LogUtils.dd(" RoomBoomManager startDownAnim() forEach url=$url") mAnimLayout?.let { animLayout -> - GlideUtils.instance().downloadFromUrl( + GlideUtils.instance().downloadFromUrl2( animLayout.context, url.trim(), object : RequestListener { diff --git a/app/src/main/java/com/chwl/app/vip/adapter/VipCenterBannerAdapter.kt b/app/src/main/java/com/chwl/app/vip/adapter/VipCenterBannerAdapter.kt index 0834c9ac3..ff2f516a2 100644 --- a/app/src/main/java/com/chwl/app/vip/adapter/VipCenterBannerAdapter.kt +++ b/app/src/main/java/com/chwl/app/vip/adapter/VipCenterBannerAdapter.kt @@ -135,8 +135,7 @@ class VipCenterBannerAdapter( }) - //todo do , liulishuo 下载器 换 glide 下载 , 注意 .trim() 去掉前后空格符 - GlideUtils.instance().downloadFromUrl(holder.vapAnimView.context,data.vipLogo.trim(),object : RequestListener { + GlideUtils.instance().downloadFromUrl2(holder.vapAnimView.context,data.vipLogo.trim(),object : RequestListener { override fun onLoadFailed( e: GlideException?, model: Any?, diff --git a/app/src/main/res/drawable-xxhdpi/ic_derss_avatar.webp b/app/src/main/res/drawable-xxhdpi/ic_derss_avatar.webp new file mode 100644 index 000000000..26a6bc24b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_derss_avatar.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_dialog_bg.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_dialog_bg.webp new file mode 100644 index 000000000..dc4634d52 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_dialog_bg.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_dialog_line.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_dialog_line.webp new file mode 100644 index 000000000..82ad71752 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_dialog_line.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_item_long_n.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_item_long_n.webp new file mode 100644 index 000000000..c5a0f7c04 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_item_long_n.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_item_long_s.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_item_long_s.webp new file mode 100644 index 000000000..c3b8ca168 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_item_long_s.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_item_n.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_item_n.webp new file mode 100644 index 000000000..110629a69 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_item_n.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_item_s.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_item_s.webp new file mode 100644 index 000000000..b57597c5a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_item_s.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_play.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_play.webp new file mode 100644 index 000000000..ebc863afa Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_play.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_bg_n.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_bg_n.webp new file mode 100644 index 000000000..f47cde5d7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_bg_n.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_bg_s.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_bg_s.webp new file mode 100644 index 000000000..20f5a1d0b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_bg_s.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_car.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_car.webp new file mode 100644 index 000000000..9da50a7f6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_car.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_card.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_card.webp new file mode 100644 index 000000000..12f24c86c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_card.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_chat.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_chat.webp new file mode 100644 index 000000000..829d43e73 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_chat.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_head.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_head.webp new file mode 100644 index 000000000..dbe4a09bb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_head.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_homepage.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_homepage.webp new file mode 100644 index 000000000..bf793c0d4 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_homepage.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_tab_nameplate.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_nameplate.webp new file mode 100644 index 000000000..783f1213d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_tab_nameplate.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dress_top_bg.webp b/app/src/main/res/drawable-xxhdpi/ic_dress_top_bg.webp new file mode 100644 index 000000000..718a7b07d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dress_top_bg.webp differ diff --git a/app/src/main/res/layout/activity_dress_up_tab.xml b/app/src/main/res/layout/activity_dress_up_tab.xml new file mode 100644 index 000000000..c46bf1acf --- /dev/null +++ b/app/src/main/res/layout/activity_dress_up_tab.xml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_search_user.xml b/app/src/main/res/layout/activity_search_user.xml new file mode 100644 index 000000000..a5212a1be --- /dev/null +++ b/app/src/main/res/layout/activity_search_user.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_user_info.xml b/app/src/main/res/layout/activity_user_info.xml index d871df75d..44005a762 100644 --- a/app/src/main/res/layout/activity_user_info.xml +++ b/app/src/main/res/layout/activity_user_info.xml @@ -537,14 +537,14 @@ + app:layout_constraintTop_toTopOf="@id/ivHeadWear" + tools:src="@color/white" /> + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_dress_up.xml b/app/src/main/res/layout/dialog_dress_up.xml new file mode 100644 index 000000000..b678f6ea4 --- /dev/null +++ b/app/src/main/res/layout/dialog_dress_up.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_decoration_common.xml b/app/src/main/res/layout/item_decoration_common.xml index 941d8827b..37cb1bf39 100644 --- a/app/src/main/res/layout/item_decoration_common.xml +++ b/app/src/main/res/layout/item_decoration_common.xml @@ -44,6 +44,7 @@ android:textColor="@color/white" android:textSize="10sp" android:visibility="gone" + tools:visibility="visible" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/layout/item_dress_up_layout.xml b/app/src/main/res/layout/item_dress_up_layout.xml new file mode 100644 index 000000000..3053091f5 --- /dev/null +++ b/app/src/main/res/layout/item_dress_up_layout.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_search_user.xml b/app/src/main/res/layout/item_search_user.xml new file mode 100644 index 000000000..b80bcc8ff --- /dev/null +++ b/app/src/main/res/layout/item_search_user.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/tab_dress_up_layout.xml b/app/src/main/res/layout/tab_dress_up_layout.xml new file mode 100644 index 000000000..1b7f3b415 --- /dev/null +++ b/app/src/main/res/layout/tab_dress_up_layout.xml @@ -0,0 +1,41 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 47883d39a..0494d2276 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -5343,4 +5343,5 @@ %s/%s يوم %s يوم <1 يوم + %s/%s يوم %s \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index b92ec79ed..a05e823f3 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -5139,4 +5139,5 @@ Toplam imza alın, ilgili ödülü alın %s/%s gün %s gün <1 gün + %s/%s gün %s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 45e947c7e..e448dee63 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -5286,5 +5286,6 @@ %s/%s天 %s天 <1天 + %s/%s天 %s \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 42ca3f369..96b036a56 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -769,5 +769,6 @@ #696969 #ff9741 #ff5656 + #d9e7f7 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 179c604a9..1f51cc8b7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5350,6 +5350,14 @@ You cannot join again within 24 hours after leaving %sDays <1Days + + Not used + userInfoBg + My Dress + VIP限制 %s + 活动获取 不可购买 + %s/%sDays %s + diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 349e86f6c..dd50af150 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -16,6 +16,8 @@ beta.api.molistar.xyz beta.img.pekolive.com + image.lecheng163.com + image.hfighting.com diff --git a/core/src/main/java/com/chwl/core/decoration/bean/DecorationInfo.kt b/core/src/main/java/com/chwl/core/decoration/bean/DecorationInfo.kt index e331a334b..a663bb170 100644 --- a/core/src/main/java/com/chwl/core/decoration/bean/DecorationInfo.kt +++ b/core/src/main/java/com/chwl/core/decoration/bean/DecorationInfo.kt @@ -10,5 +10,12 @@ data class DecorationInfo( val iosPic: String = "", val name: String = "", val pic: String = "", - val dressLimitStatus: Int = 0 + val dressLimitStatus: Int = 0, + val effect: String = "", //装扮动效图片 + val effectType: Int = 0, //动效类型 + val discountPrice: Double = 0.0, //折扣价格 + val vipLevel: Int = 0, //VIP等级 + val discount: Int = 0, // 折扣百分比 + val vipLimit: Int = 0, //VIP等级限定 ,0=不限定 + val obtainWay: Int = 0 //1-普通 2-活动 ) \ No newline at end of file diff --git a/core/src/main/java/com/chwl/core/decoration/bean/DressUpInfo.java b/core/src/main/java/com/chwl/core/decoration/bean/DressUpInfo.java new file mode 100644 index 000000000..8cd3d7caa --- /dev/null +++ b/core/src/main/java/com/chwl/core/decoration/bean/DressUpInfo.java @@ -0,0 +1,45 @@ +package com.chwl.core.decoration.bean; + +import lombok.Data; + +@Data +public class DressUpInfo { + public boolean isNull; + public boolean isMy; + public boolean isSelect; + public int dressLimitStatus; + + public String pic; + public String name; + public String word; + public int price; + public int buyDay; + public int originalPrice; + + public String effect; //装扮动效图片 + /** + * 特效类型 1-mp4 2-svga + */ + public int effectType; + public int dressType; // 商品类型 + public int dressId; // 商品类型 + public int discountPrice; //折扣价格 + public int vipLevel; //当前用户VIP等级 + public int discount; // 折扣百分比 + public int vipLimit; //VIP等级限定 ,0=不限定 + /** + * //1-普通 2-活动 + */ + public int obtainWay; + public int id; //商品类型id + + + public int expireDays; //剩余天数 + public boolean hasExpired; //是否过期 + + public @interface EffectType{ + int MP4 = 1; + int SVGA = 2; + } + +} diff --git a/core/src/main/java/com/chwl/core/decoration/bean/ShopMine.java b/core/src/main/java/com/chwl/core/decoration/bean/ShopMine.java new file mode 100644 index 000000000..1c54b5f19 --- /dev/null +++ b/core/src/main/java/com/chwl/core/decoration/bean/ShopMine.java @@ -0,0 +1,20 @@ +package com.chwl.core.decoration.bean; + +import lombok.Data; + +@Data +public class ShopMine { + public int dressId; //装扮id + public int dressType; //装扮类型 + public int obtainWay; //获取方式 1-普通 2-活动 + + public String pic; + public String name; + public String effect; + public boolean used; + public int expireDays = -1; //剩余天数 + public int effectType; + public boolean hasExpired; //是否过期 + + +} diff --git a/core/src/main/java/com/chwl/core/decoration/headwear/HeadwearModel.java b/core/src/main/java/com/chwl/core/decoration/headwear/HeadwearModel.java index 567f1c728..03960926c 100644 --- a/core/src/main/java/com/chwl/core/decoration/headwear/HeadwearModel.java +++ b/core/src/main/java/com/chwl/core/decoration/headwear/HeadwearModel.java @@ -122,6 +122,7 @@ public class HeadwearModel extends BaseModel implements IHeadwearModel { /** * 使用已经购买的头饰 + * id == 0标识不使用头饰 * * @param headwearId * @return diff --git a/core/src/main/java/com/chwl/core/home/bean/TabInfo.java b/core/src/main/java/com/chwl/core/home/bean/TabInfo.java index 74526d38d..f90b72896 100644 --- a/core/src/main/java/com/chwl/core/home/bean/TabInfo.java +++ b/core/src/main/java/com/chwl/core/home/bean/TabInfo.java @@ -36,6 +36,7 @@ public class TabInfo implements Parcelable,Serializable { protected String name; protected String pict; protected int seq; + protected int res; protected int type; protected boolean status; protected boolean istop; @@ -49,6 +50,12 @@ public class TabInfo implements Parcelable,Serializable { this.name = name; } + public TabInfo(int id, String name,int res) { + this.id = id; + this.name = name; + this.res = res; + } + protected TabInfo(Parcel in) { id = in.readInt(); diff --git a/core/src/main/java/com/chwl/core/room/face/DynamicFaceModel.java b/core/src/main/java/com/chwl/core/room/face/DynamicFaceModel.java index caf44b9ba..464f1c17f 100644 --- a/core/src/main/java/com/chwl/core/room/face/DynamicFaceModel.java +++ b/core/src/main/java/com/chwl/core/room/face/DynamicFaceModel.java @@ -7,21 +7,12 @@ import android.annotation.SuppressLint; import android.text.TextUtils; import android.util.SparseArray; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; -import com.chwl.core.utils.MyUriUtils; -import com.chwl.library.common.glide.GlideUtils; -import com.google.gson.Gson; -import com.netease.nim.uikit.common.util.string.StringUtil; -import com.netease.nimlib.sdk.chatroom.ChatRoomMessageBuilder; -import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage; -import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum; -import com.orhanobut.logger.Logger; import com.chwl.core.DemoCache; import com.chwl.core.auth.AuthModel; import com.chwl.core.base.BaseModel; @@ -41,12 +32,20 @@ import com.chwl.core.room.queue.bean.MicMemberInfo; import com.chwl.core.user.UserModel; import com.chwl.core.user.bean.UserInfo; import com.chwl.core.utils.LogUtils; +import com.chwl.core.utils.MyUriUtils; import com.chwl.core.vip.bean.UserVipInfo; +import com.chwl.library.common.glide.GlideUtils; import com.chwl.library.utils.ListUtils; import com.chwl.library.utils.NetworkUtils; import com.chwl.library.utils.codec.DESUtils; import com.chwl.library.utils.codec.MD5Utils; import com.chwl.library.utils.config.BasicConfig; +import com.google.gson.Gson; +import com.netease.nim.uikit.common.util.string.StringUtil; +import com.netease.nimlib.sdk.chatroom.ChatRoomMessageBuilder; +import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage; +import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum; +import com.orhanobut.logger.Logger; import org.greenrobot.eventbus.EventBus; @@ -55,7 +54,6 @@ import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -67,11 +65,6 @@ import io.reactivex.Single; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.BiConsumer; import io.reactivex.functions.Consumer; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; import okhttp3.ResponseBody; import retrofit2.http.GET; import retrofit2.http.Headers; @@ -634,7 +627,7 @@ public class DynamicFaceModel extends BaseModel implements IDynamicFaceModel { String url = onlineFacesList.getZipUrl(); - GlideUtils.instance().downloadFromUrl(getContext(), url, new RequestListener() { + GlideUtils.instance().downloadFromUrl2(getContext(), url, new RequestListener() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { Logger.d("face.zip download onLoadFailed"); diff --git a/core/src/main/java/com/chwl/core/utils/ImageSplitter.java b/core/src/main/java/com/chwl/core/utils/ImageSplitter.java index b7685d9a1..af038dff3 100644 --- a/core/src/main/java/com/chwl/core/utils/ImageSplitter.java +++ b/core/src/main/java/com/chwl/core/utils/ImageSplitter.java @@ -6,6 +6,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import com.chwl.core.manager.AvRoomDataManager; +import com.chwl.library.utils.BitmapLruCacheHelper; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/com/chwl/core/utils/BitmapLruCacheHelper.java b/library/src/main/java/com/chwl/library/utils/BitmapLruCacheHelper.java similarity index 98% rename from core/src/main/java/com/chwl/core/utils/BitmapLruCacheHelper.java rename to library/src/main/java/com/chwl/library/utils/BitmapLruCacheHelper.java index a003711a0..9684c3a90 100644 --- a/core/src/main/java/com/chwl/core/utils/BitmapLruCacheHelper.java +++ b/library/src/main/java/com/chwl/library/utils/BitmapLruCacheHelper.java @@ -1,4 +1,4 @@ -package com.chwl.core.utils; +package com.chwl.library.utils; import android.graphics.Bitmap; import android.util.Log; diff --git a/library/src/main/java/com/chwl/library/widget/SVGAView.kt b/library/src/main/java/com/chwl/library/widget/SVGAView.kt index dd0be7b66..34bc95767 100644 --- a/library/src/main/java/com/chwl/library/widget/SVGAView.kt +++ b/library/src/main/java/com/chwl/library/widget/SVGAView.kt @@ -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 { + logD("SVGAView loadSVGAUrl url:$url") + GlideUtils.instance().downloadFromUrl2(context,url,object : RequestListener { override fun onLoadFailed( e: GlideException?, model: Any?, target: Target?, 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 { + override fun onLoadFailed( + e: GlideException?, + model: Any, + target: Target, + isFirstResource: Boolean + ): Boolean { + this@SVGAView.resourceUrl = null + onViewStateChanged(0) + return false + } + + override fun onResourceReady( + resource: Bitmap?, + model: Any, + target: Target, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { + if (resource == null) return false + val split: List = 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 { + val pieces: MutableList = 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? diff --git a/library/src/main/java/com/chwl/library/widget/tab/SmartTabIndicationInterpolator.java b/library/src/main/java/com/chwl/library/widget/tab/SmartTabIndicationInterpolator.java new file mode 100644 index 000000000..2ae177692 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/SmartTabIndicationInterpolator.java @@ -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; + } + + } +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/SmartTabLayout.java b/library/src/main/java/com/chwl/library/widget/tab/SmartTabLayout.java new file mode 100644 index 000000000..92c8062f7 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/SmartTabLayout.java @@ -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. + *

+ * 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. + *

+ * 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. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + *

+ * Forked from Google Samples > SlidingTabsBasic > + * SlidingTabLayout + */ +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; + } + } + } + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/SmartTabLayout2.java b/library/src/main/java/com/chwl/library/widget/tab/SmartTabLayout2.java new file mode 100644 index 000000000..2adac7d3a --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/SmartTabLayout2.java @@ -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. + *

+ * 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. + *

+ * 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. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + *

+ * Forked from Google Samples > SlidingTabsBasic > + * SlidingTabLayout + */ +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; + } + } +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/SmartTabStrip.java b/library/src/main/java/com/chwl/library/widget/tab/SmartTabStrip.java new file mode 100644 index 000000000..8ecaf053d --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/SmartTabStrip.java @@ -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; + +/** + *

+ * Forked from Google Samples > SlidingTabsBasic > + * SlidingTabStrip + */ +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; + } + } +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/TabColorizer.java b/library/src/main/java/com/chwl/library/widget/tab/TabColorizer.java new file mode 100644 index 000000000..24276ea41 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/TabColorizer.java @@ -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); + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/TabTitleProvider.java b/library/src/main/java/com/chwl/library/widget/tab/TabTitleProvider.java new file mode 100644 index 000000000..a97434194 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/TabTitleProvider.java @@ -0,0 +1,8 @@ +package com.chwl.library.widget.tab; + +import androidx.annotation.Nullable; + +public interface TabTitleProvider { + @Nullable + CharSequence getPageTitle(int position); +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/Utils.java b/library/src/main/java/com/chwl/library/widget/tab/Utils.java new file mode 100644 index 000000000..e5e5528a5 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/Utils.java @@ -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; + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/FragmentPageAdapter.kt b/library/src/main/java/com/chwl/library/widget/tab/util/FragmentPageAdapter.kt new file mode 100644 index 000000000..da0c2e7e4 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/FragmentPageAdapter.kt @@ -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?) : + FragmentStateAdapter(fragmentManager) { + + + override fun getItemCount(): Int { + return fgList?.size?:0 + } + + override fun createFragment(position: Int): Fragment { + return fgList?.get(position) ?: Fragment() + } + +} \ No newline at end of file diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/PagerItem.java b/library/src/main/java/com/chwl/library/widget/tab/util/PagerItem.java new file mode 100644 index 000000000..69772b279 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/PagerItem.java @@ -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; + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/PagerItems.java b/library/src/main/java/com/chwl/library/widget/tab/util/PagerItems.java new file mode 100644 index 000000000..6a9636511 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/PagerItems.java @@ -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 extends ArrayList { + + private final Context context; + + protected PagerItems(Context context) { + this.context = context; + } + + public Context getContext() { + return context; + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItem.java b/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItem.java new file mode 100644 index 000000000..ab5eabd4b --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItem.java @@ -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); + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItemAdapter.java b/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItemAdapter.java new file mode 100644 index 000000000..4dc50fad7 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItemAdapter.java @@ -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> 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)); + 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 weakRefItem = holder.get(position); + return (weakRefItem != null) ? weakRefItem.get() : null; + } + + protected ViewPagerItem getPagerItem(int position) { + return pages.get(position); + } +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItems.java b/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItems.java new file mode 100644 index 000000000..e456e9de5 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/ViewPagerItems.java @@ -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 { + + 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; + } + + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/v4/Bundler.java b/library/src/main/java/com/chwl/library/widget/tab/util/v4/Bundler.java new file mode 100644 index 000000000..527ae53e4 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/v4/Bundler.java @@ -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 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 value) { + bundle.putSparseParcelableArray(key, value); + return this; + } + + /** + * Inserts an ArrayList 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 object, or null + * @return this + */ + public Bundler putIntegerArrayList(String key, ArrayList value) { + bundle.putIntegerArrayList(key, value); + return this; + } + + /** + * Inserts an ArrayList 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 object, or null + * @return this + */ + public Bundler putStringArrayList(String key, ArrayList value) { + bundle.putStringArrayList(key, value); + return this; + } + + /** + * Inserts an ArrayList 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 object, or null + * @return this + */ + @TargetApi(8) + public Bundler putCharSequenceArrayList(String key, ArrayList 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. + * + *

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.

+ * + * @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 into(T fragment) { + fragment.setArguments(get()); + return fragment; + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItem.java b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItem.java new file mode 100644 index 000000000..7ca6266e7 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItem.java @@ -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 clazz) { + return of(title, DEFAULT_WIDTH, clazz); + } + + public static FragmentPagerItem of(CharSequence title, Class clazz, + Bundle args) { + return of(title, DEFAULT_WIDTH, clazz, args); + } + + public static FragmentPagerItem of(CharSequence title, float width, + Class clazz) { + return of(title, width, clazz, new Bundle()); + } + + public static FragmentPagerItem of(CharSequence title, float width, + Class 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); + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItemAdapter.java b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItemAdapter.java new file mode 100644 index 000000000..dd99fdfe9 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItemAdapter.java @@ -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> 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) 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 weakRefItem = holder.get(position); + return (weakRefItem != null) ? weakRefItem.get() : null; + } + + protected FragmentPagerItem getPagerItem(int position) { + return pages.get(position); + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItems.java b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItems.java new file mode 100644 index 000000000..c5c987761 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentPagerItems.java @@ -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 { + + 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 clazz) { + return add(FragmentPagerItem.of(items.getContext().getString(title), clazz)); + } + + public Creator add(@StringRes int title, Class clazz, Bundle args) { + return add(FragmentPagerItem.of(items.getContext().getString(title), clazz, args)); + } + + public Creator add(@StringRes int title, float width, Class clazz) { + return add(FragmentPagerItem.of(items.getContext().getString(title), width, clazz)); + } + + public Creator add(@StringRes int title, float width, Class clazz, + Bundle args) { + return add(FragmentPagerItem.of(items.getContext().getString(title), width, clazz, args)); + } + + public Creator add(CharSequence title, Class clazz) { + return add(FragmentPagerItem.of(title, clazz)); + } + + public Creator add(CharSequence title, Class 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; + } + + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentStatePagerItemAdapter.java b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentStatePagerItemAdapter.java new file mode 100644 index 000000000..fe29eb7b5 --- /dev/null +++ b/library/src/main/java/com/chwl/library/widget/tab/util/v4/FragmentStatePagerItemAdapter.java @@ -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> 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) 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 weakRefItem = holder.get(position); + return (weakRefItem != null) ? weakRefItem.get() : null; + } + + protected FragmentPagerItem getPagerItem(int position) { + return pages.get(position); + } + +} diff --git a/library/src/main/java/com/chwl/library/widget/text/DrawableTextView.java b/library/src/main/java/com/chwl/library/widget/text/DrawableTextView.java index 562313377..0ab96cb24 100644 --- a/library/src/main/java/com/chwl/library/widget/text/DrawableTextView.java +++ b/library/src/main/java/com/chwl/library/widget/text/DrawableTextView.java @@ -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); diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 185b1dfd3..68afd3200 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -191,4 +191,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/module_common/java/com/chwl/library/common/glide/AnimEffectUtil.kt b/library/src/module_common/java/com/chwl/library/common/glide/AnimEffectUtil.kt new file mode 100644 index 000000000..d4ac20d19 --- /dev/null +++ b/library/src/module_common/java/com/chwl/library/common/glide/AnimEffectUtil.kt @@ -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() +// private var glideRequests = mutableListOf?>() +// +// private var tempExtMap = mutableMapOf() +// 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(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(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>() +// val textMap = mutableMapOf() +// val paramsMap = mutableMapOf() +// 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 { +// override fun onLoadFailed(e: GlideException?, model: Any?, target: Target, 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?, 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) { +// } +// +// 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() +// } +// } +//} \ No newline at end of file diff --git a/library/src/module_common/java/com/chwl/library/common/glide/GlideUtils.kt b/library/src/module_common/java/com/chwl/library/common/glide/GlideUtils.kt index f8d38edcc..c4409d8f6 100644 --- a/library/src/module_common/java/com/chwl/library/common/glide/GlideUtils.kt +++ b/library/src/module_common/java/com/chwl/library/common/glide/GlideUtils.kt @@ -1376,6 +1376,28 @@ class GlideUtils { glideConfig?.downloadOnly()?.load(url)?.listener(listener)?.preload() } + /** + * 使用Glide下载图片,返回File + */ + fun downloadFromUrl2( + context: Context?, + url: String?, + listener: RequestListener? + ) { + 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?) { diff --git a/library/src/module_common/java/com/chwl/library/common/util/OtherExt.kt b/library/src/module_common/java/com/chwl/library/common/util/OtherExt.kt index 8e5dc8db9..f2f90cc7e 100644 --- a/library/src/module_common/java/com/chwl/library/common/util/OtherExt.kt +++ b/library/src/module_common/java/com/chwl/library/common/util/OtherExt.kt @@ -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?.isVerify() : Boolean{ return this?.isEmpty() == false } diff --git a/libs/lib_utils/src/main/java/com/example/lib_utils/spannable/SpannableTextBuilder.kt b/libs/lib_utils/src/main/java/com/example/lib_utils/spannable/SpannableTextBuilder.kt index 1de51695d..096a04b81 100644 --- a/libs/lib_utils/src/main/java/com/example/lib_utils/spannable/SpannableTextBuilder.kt +++ b/libs/lib_utils/src/main/java/com/example/lib_utils/spannable/SpannableTextBuilder.kt @@ -74,11 +74,74 @@ class SpannableTextBuilder(private val textView: TextView) { textSize, textStyle, underline, + null, clickListener ) return this } + fun setTextStyle( + text: String, + @ColorInt textColor: Int? = null, + @ColorInt backgroundColor: Int? = null, + textSize: Int? = null, + textStyle: Int? = null, + underline: Boolean? = null, + clickListener: ((String) -> Unit)? = null + ): SpannableTextBuilder { + if (text.isEmpty()) { + return this + } + val start = spannableBuilder.indexOf(text) + if (start == -1) { + return this + } + val end = start + text.length + return setTextStyle( + text, + start, + end, + textColor, + backgroundColor, + textSize, + textStyle, + underline, + null, + clickListener + ) + } + fun setTextStyle( + text: String, + @ColorInt textColor: Int? = null, + @ColorInt backgroundColor: Int? = null, + textSize: Int? = null, + textStyle: Int? = null, + underline: Boolean? = null, + delLine: Boolean? = null, + clickListener: ((String) -> Unit)? = null + ): SpannableTextBuilder { + if (text.isEmpty()) { + return this + } + val start = spannableBuilder.indexOf(text) + if (start == -1) { + return this + } + val end = start + text.length + return setTextStyle( + text, + start, + end, + textColor, + backgroundColor, + textSize, + textStyle, + underline, + delLine, + clickListener + ) + } + /** * 添加图片 * @param drawable 图片 @@ -187,6 +250,7 @@ class SpannableTextBuilder(private val textView: TextView) { textSize: Int? = null, textStyle: Int? = null, underline: Boolean? = null, + delLine: Boolean? = null, clickListener: ((String) -> Unit)? = null ): SpannableTextBuilder { if (start < 0 || end > spannableBuilder.length) { @@ -236,6 +300,10 @@ class SpannableTextBuilder(private val textView: TextView) { if (underline == true) { spannableBuilder.setSpan(UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } + // 删除线 + if (delLine == true) { + spannableBuilder.setSpan(StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } // 点击事件 if (clickListener != null) { @@ -251,35 +319,7 @@ class SpannableTextBuilder(private val textView: TextView) { return this } - fun setTextStyle( - text: String, - @ColorInt textColor: Int? = null, - @ColorInt backgroundColor: Int? = null, - textSize: Int? = null, - textStyle: Int? = null, - underline: Boolean? = null, - clickListener: ((String) -> Unit)? = null - ): SpannableTextBuilder { - if (text.isEmpty()) { - return this - } - val start = spannableBuilder.indexOf(text) - if (start == -1) { - return this - } - val end = start + text.length - return setTextStyle( - text, - start, - end, - textColor, - backgroundColor, - textSize, - textStyle, - underline, - clickListener - ) - } + fun build(): SpannableStringBuilder { return spannableBuilder diff --git a/mode.json b/mode.json index a364a26d5..b202e578a 100644 --- a/mode.json +++ b/mode.json @@ -1,98 +1,181 @@ -[ - { - "id": 359, - "uid": 3224, - "nameplateId": 101, - "isCustomWord": false, - "word": " ", - "remark": " ", - "expireTime": 1731998395000, - "createTime": 1731047995000, - "updateTime": 1731318342000, - "nameplateName": "schoolgirl-Advanced", - "nameplateImage": "https://image.pekolive.com/gonghuizhangmingpaikaobei.png", - "expireDays": 0, - "iconPic": "https://image.pekolive.com/gonghuizhangmingpaikaobei.png", - "fixedWord": " ", - "nameplateType": "1" - }, - { - "id": 371, - "uid": 3224, - "nameplateId": 96, - "isCustomWord": false, - "remark": " ", - "expireTime": 1731143136000, - "createTime": 1731056736000, - "updateTime": 1731056736000, - "nameplateName": "king", - "nameplateImage": "https://image.pekolive.com/zu689(11).png", - "expireDays": 0, - "iconPic": "https://image.pekolive.com/zu689(11).png", - "nameplateType": "1" - }, - { - "id": 369, - "uid": 3224, - "nameplateId": 100, - "isCustomWord": false, - "remark": " ", - "expireTime": 1731143131000, - "createTime": 1731056731000, - "updateTime": 1731056731000, - "nameplateName": "schoolgirl-Advanced", - "nameplateImage": "https://image.pekolive.com/huayunu:shen.png", - "expireDays": 0, - "iconPic": "https://image.pekolive.com/guanfangzhuli-bajisitan.png", - "nameplateType": "1" - }, - { - "id": 367, - "uid": 3224, - "nameplateId": 93, - "isCustomWord": false, - "remark": " ", - "expireTime": 1731143124000, - "createTime": 1731056724000, - "updateTime": 1731056724000, - "nameplateName": "queen", - "nameplateImage": "https://image.pekolive.com/zu688(3).png", - "expireDays": 0, - "iconPic": "https://image.pekolive.com/zu688(3).png", - "nameplateType": "1" - }, - { - "id": 365, - "uid": 3224, - "nameplateId": 92, - "isCustomWord": true, - "word": " العناوين", - "remark": " العناوين", - "expireTime": 1731143086000, - "createTime": 1731056686000, - "updateTime": 1731056686000, - "nameplateName": "給糖不搗蛋", - "nameplateImage": "https://image.pekolive.com/8350643695e74ec9bb78b9944fc9e842.png", - "expireDays": 0, - "iconPic": "https://image.pekolive.com/zu689(5).png", - "fixedWord": "العناوين", - "nameplateType": "1" - }, - { - "id": 124, - "uid": 3224, - "nameplateId": 101, - "isCustomWord": false, - "word": " ", - "remark": " ", - "expireTime": 1729340700000, - "createTime": 1728476700000, - "updateTime": 1731318342000, - "nameplateName": "schoolgirl-Advanced", - "nameplateImage": "https://image.pekolive.com/gonghuizhangmingpaikaobei.png", - "expireDays": 0, - "iconPic": "https://image.pekolive.com/gonghuizhangmingpaikaobei.png", - "fixedWord": " ", - "nameplateType": "1" - } -] \ No newline at end of file +{ + "success": true, + "code": 200, + "message": "success", + "data": [ + { + "dressId": 195, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.pekolive.com/Vip5headdress.png", + "effectType": 2, + "effect": "https://image.pekolive.com/Vip5headdress.svga", + "name": "VIP5頭飾", + "used": true, + "expireDays": 22, + "hasExpired": false, + "expireTime": 1733976000000 + }, + { + "dressId": 196, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.pekolive.com/Vip6headdress.png", + "effectType": 2, + "effect": "https://image.pekolive.com/Vip6headdress.svga", + "name": "VIP6頭飾", + "used": false, + "expireDays": 25, + "hasExpired": false, + "expireTime": 1734235200000 + }, + { + "dressId": 193, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.pekolive.com/Vip3headdress.png", + "effectType": 2, + "effect": "https://image.pekolive.com/Vip3headdress.svga", + "name": "VIP3頭飾", + "used": false, + "expireDays": 23, + "hasExpired": false, + "expireTime": 1734062400000 + }, + { + "dressId": 26, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.hfighting.com/1df2071a-610d-41be-b763-8437ef9f3524", + "effectType": 2, + "effect": "https://image.hfighting.com/6d9ff134-80c1-44d5-97b0-981817e03bd4", + "name": "幸運星人", + "used": false, + "expireDays": 22, + "hasExpired": false, + "expireTime": 1733919509000 + }, + { + "dressId": 31, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.hfighting.com/2ec2f8cd-e660-4af2-bd49-b9eed8de10a2", + "effectType": 2, + "effect": "https://image.hfighting.com/34369437-e9a3-4bb8-80a2-f310bf8e90fc", + "name": "財氣沖天", + "used": false, + "expireDays": 13, + "hasExpired": false, + "expireTime": 1733173500000 + }, + { + "dressId": 27, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.hfighting.com/06fe0618-cd55-4e5e-84bf-1071f821d608", + "effectType": 2, + "effect": "https://image.hfighting.com/22f0fbb1-fdb1-46f9-b037-03f0693effeb", + "name": "潮舞星迎", + "used": false, + "expireDays": 13, + "hasExpired": false, + "expireTime": 1733131248000 + }, + { + "dressId": 123, + "dressType": 0, + "obtainWay": 1, + "pic": "http://beta.img.pekolive.com/FklHnzcmijfk_0QGRePXuzG3LSdN?imageslim", + "effectType": 2, + "effect": "http://beta.img.pekolive.com/FmDgjyKd_p0SvpKmC5v0rwhszdH9?imageslim", + "name": "花语小兔", + "used": false, + "expireDays": 9, + "hasExpired": false, + "expireTime": 1732780728000 + }, + { + "dressId": 110, + "dressType": 0, + "obtainWay": 1, + "pic": "http://beta.img.pekolive.com/FrTtFcnSI88bITGonp5PJjdbGLLR?imageslim", + "effectType": 2, + "effect": "http://beta.img.pekolive.com/FrTtFcnSI88bITGonp5PJjdbGLLR?imageslim", + "name": "豪氣季軍", + "used": false, + "expireDays": 8, + "hasExpired": false, + "expireTime": 1732760886000 + }, + { + "dressId": 30, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.hfighting.com/0fcea959-e82a-410e-a3f5-52f751a6c327", + "effectType": 2, + "effect": "https://image.hfighting.com/efacf269-c628-4940-984f-b817aabf2127", + "name": "拉丁之神", + "used": false, + "expireDays": 6, + "hasExpired": false, + "expireTime": 1732526621000 + }, + { + "dressId": 28, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.hfighting.com/4dd02f23-e30f-4df7-9326-53a1f4ae8ebf", + "effectType": 2, + "effect": "https://image.hfighting.com/c17f08d6-1854-4d03-a760-e02ad5fdf081", + "name": "月魔法帽", + "used": false, + "expireDays": 6, + "hasExpired": false, + "expireTime": 1732526452000 + }, + { + "dressId": 42, + "dressType": 0, + "obtainWay": 1, + "pic": "http://beta.img.pekolive.com/Fq_EzPCxN99nV84CgO3c3z242pEh?imageslim", + "effectType": 2, + "effect": "http://beta.img.pekolive.com/Fq_EzPCxN99nV84CgO3c3z242pEh?imageslim", + "name": "海浪", + "used": false, + "expireDays": 2, + "hasExpired": false, + "expireTime": 1732175930000 + }, + { + "dressId": 29, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.hfighting.com/a51c4b19-6862-41fa-83cb-2475db2320da", + "effectType": 2, + "effect": "https://image.hfighting.com/5df51843-1c4c-40c1-b1dd-5420ce093de2", + "name": "神囚魔法", + "used": false, + "expireDays": 1, + "hasExpired": false, + "expireTime": 1732097432000 + }, + { + "dressId": 102, + "dressType": 0, + "obtainWay": 1, + "pic": "http://beta.img.pekolive.com/FrQQrQ0yEhTCLxyKHygT76kZLHoy?imageslim", + "effectType": 2, + "effect": "http://beta.img.pekolive.com/FqJrMr8YmRr6WBR0FOMaYL05HPxy?imageslim", + "name": "星河白羽", + "used": false, + "expireDays": 0, + "hasExpired": true, + "expireTime": 1731664948000 + }, + { + "dressId": 44, + "dressType": 0, + "obtainWay": 1, + "pic": "https://image.hfighting.com/bb55be24-3418-4f03-bf46-1bb491b95ba2", + "effectType": 2, + "effect": "https:/ diff --git a/nim_uikit/src/com/netease/nim/uikit/common/ui/imageview/CircleImageView.java b/nim_uikit/src/com/netease/nim/uikit/common/ui/imageview/CircleImageView.java index 547115658..367df10ac 100644 --- a/nim_uikit/src/com/netease/nim/uikit/common/ui/imageview/CircleImageView.java +++ b/nim_uikit/src/com/netease/nim/uikit/common/ui/imageview/CircleImageView.java @@ -31,11 +31,12 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.util.AttributeSet; -import android.widget.ImageView; + +import androidx.appcompat.widget.AppCompatImageView; import com.netease.nim.uikit.R; -public class CircleImageView extends ImageView { +public class CircleImageView extends AppCompatImageView { private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;