feat: 侧边滑动返回 & 两侧支持

This commit is contained in:
HuanCheng65 2022-06-16 12:16:17 +08:00
parent d55b0d6df8
commit 5faef59cbc
No known key found for this signature in database
GPG Key ID: E9031EF91A805148
15 changed files with 542 additions and 88 deletions

View File

@ -84,9 +84,6 @@ dependencies {
//Local Files //Local Files
implementation fileTree(include: ["*.jar"], dir: "libs") implementation fileTree(include: ["*.jar"], dir: "libs")
//
implementation "me.imid.swipebacklayout.lib:library:1.3.0"
// Glide // Glide
implementation "com.android.support:support-annotations:28.0.0" implementation "com.android.support:support-annotations:28.0.0"
kapt "com.android.support:support-annotations:28.0.0" kapt "com.android.support:support-annotations:28.0.0"

View File

@ -23,6 +23,7 @@ import androidx.annotation.CallSuper
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import butterknife.ButterKnife import butterknife.ButterKnife
import cn.jzvd.Jzvd import cn.jzvd.Jzvd
@ -30,16 +31,16 @@ import com.gyf.immersionbar.ImmersionBar
import com.huanchengfly.tieba.post.BaseApplication import com.huanchengfly.tieba.post.BaseApplication
import com.huanchengfly.tieba.post.BaseApplication.Companion.instance import com.huanchengfly.tieba.post.BaseApplication.Companion.instance
import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.ui.slideback.SlideBack
import com.huanchengfly.tieba.post.ui.theme.interfaces.ExtraRefreshable import com.huanchengfly.tieba.post.ui.theme.interfaces.ExtraRefreshable
import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils
import com.huanchengfly.tieba.post.utils.* import com.huanchengfly.tieba.post.utils.*
import com.huanchengfly.tieba.post.widgets.VoicePlayerView import com.huanchengfly.tieba.post.widgets.VoicePlayerView
import com.huanchengfly.tieba.post.widgets.theme.TintToolbar import com.huanchengfly.tieba.post.widgets.theme.TintToolbar
import kotlinx.coroutines.* import kotlinx.coroutines.*
import me.imid.swipebacklayout.lib.app.SwipeBackActivity
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
abstract class BaseActivity : SwipeBackActivity(), ExtraRefreshable, CoroutineScope { abstract class BaseActivity : AppCompatActivity(), ExtraRefreshable, CoroutineScope {
val job = Job() val job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job get() = Dispatchers.Main + job
@ -99,6 +100,8 @@ abstract class BaseActivity : SwipeBackActivity(), ExtraRefreshable, CoroutineSc
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
SlideBack.create()
.attachToActivity(this)
fixBackground() fixBackground()
getDeviceDensity() getDeviceDensity()
instance.addActivity(this) instance.addActivity(this)

View File

@ -166,7 +166,6 @@ open class MainActivity : BaseActivity(), NavigationBarView.OnItemSelectedListen
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setSwipeBackEnable(false)
ThemeUtil.setTranslucentThemeBackground(findViewById(R.id.background)) ThemeUtil.setTranslucentThemeBackground(findViewById(R.id.background))
findView() findView()
initView() initView()

View File

@ -10,14 +10,11 @@ import android.view.inputmethod.EditorInfo
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.widget.TextViewCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView import butterknife.BindView
import cn.dreamtobe.kpswitch.util.KeyboardUtil import cn.dreamtobe.kpswitch.util.KeyboardUtil
import com.alibaba.android.vlayout.DelegateAdapter import com.alibaba.android.vlayout.DelegateAdapter
import com.alibaba.android.vlayout.VirtualLayoutManager import com.alibaba.android.vlayout.VirtualLayoutManager
import com.alibaba.android.vlayout.layout.LinearLayoutHelper
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.huanchengfly.tieba.post.R import com.huanchengfly.tieba.post.R
@ -26,8 +23,6 @@ import com.huanchengfly.tieba.post.adapters.HeaderDelegateAdapter
import com.huanchengfly.tieba.post.adapters.HeaderDelegateAdapter.Companion.NO_ICON import com.huanchengfly.tieba.post.adapters.HeaderDelegateAdapter.Companion.NO_ICON
import com.huanchengfly.tieba.post.adapters.SearchHistoryAdapter import com.huanchengfly.tieba.post.adapters.SearchHistoryAdapter
import com.huanchengfly.tieba.post.adapters.SingleLayoutDelegateAdapter import com.huanchengfly.tieba.post.adapters.SingleLayoutDelegateAdapter
import com.huanchengfly.tieba.post.adapters.base.BaseSingleTypeDelegateAdapter
import com.huanchengfly.tieba.post.api.models.web.HotMessageListBean
import com.huanchengfly.tieba.post.components.AutoLineFeedLayoutManager import com.huanchengfly.tieba.post.components.AutoLineFeedLayoutManager
import com.huanchengfly.tieba.post.components.MyViewHolder import com.huanchengfly.tieba.post.components.MyViewHolder
import com.huanchengfly.tieba.post.components.dividers.SpacesItemDecoration import com.huanchengfly.tieba.post.components.dividers.SpacesItemDecoration
@ -35,16 +30,13 @@ import com.huanchengfly.tieba.post.dpToPx
import com.huanchengfly.tieba.post.fragments.SearchForumFragment import com.huanchengfly.tieba.post.fragments.SearchForumFragment
import com.huanchengfly.tieba.post.fragments.SearchThreadFragment import com.huanchengfly.tieba.post.fragments.SearchThreadFragment
import com.huanchengfly.tieba.post.fragments.SearchUserFragment import com.huanchengfly.tieba.post.fragments.SearchUserFragment
import com.huanchengfly.tieba.post.getColorCompat
import com.huanchengfly.tieba.post.interfaces.ISearchFragment import com.huanchengfly.tieba.post.interfaces.ISearchFragment
import com.huanchengfly.tieba.post.models.database.SearchHistory import com.huanchengfly.tieba.post.models.database.SearchHistory
import com.huanchengfly.tieba.post.toastShort import com.huanchengfly.tieba.post.toastShort
import com.huanchengfly.tieba.post.ui.theme.utils.ColorStateListUtils import com.huanchengfly.tieba.post.ui.theme.utils.ColorStateListUtils
import com.huanchengfly.tieba.post.utils.AnimUtil import com.huanchengfly.tieba.post.utils.AnimUtil
import com.huanchengfly.tieba.post.utils.NavigationHelper
import com.huanchengfly.tieba.post.utils.ThemeUtil import com.huanchengfly.tieba.post.utils.ThemeUtil
import com.huanchengfly.tieba.post.utils.anim.animSet import com.huanchengfly.tieba.post.utils.anim.animSet
import com.huanchengfly.tieba.post.utils.getIntermixedColorBackground
import com.huanchengfly.tieba.post.widgets.MyViewPager import com.huanchengfly.tieba.post.widgets.MyViewPager
import org.litepal.LitePal import org.litepal.LitePal
@ -69,8 +61,6 @@ class NewSearchActivity : BaseActivity(), TabLayout.OnTabSelectedListener {
@BindView(R.id.bottom_app_bar) @BindView(R.id.bottom_app_bar)
lateinit var bottomAppBar: View lateinit var bottomAppBar: View
var hotMessageListBean: HotMessageListBean? = null
private var keyword: String? = null private var keyword: String? = null
set(value) { set(value) {
field = value field = value
@ -260,51 +250,6 @@ class NewSearchActivity : BaseActivity(), TabLayout.OnTabSelectedListener {
} }
} }
inner class HotTopicDelegateAdapter(
list: List<HotMessageListBean.HotMessageRetBean>? = null
) : BaseSingleTypeDelegateAdapter<HotMessageListBean.HotMessageRetBean>(
this,
LinearLayoutHelper(),
list
) {
override fun convert(viewHolder: MyViewHolder, item: HotMessageListBean.HotMessageRetBean, position: Int) {
viewHolder.setText(R.id.hot_order, "${position + 1}")
viewHolder.setText(R.id.hot_title, item.mulName)
viewHolder.setText(R.id.hot_desc, item.topicInfo.topicDesc)
val textView = viewHolder.getView<TextView>(R.id.hot_order)
if (position > 2) {
TextViewCompat.setTextAppearance(textView, R.style.TextAppearance_Bold)
textView.setTextColor(context.getColorCompat(R.color.tieba))
} else {
TextViewCompat.setTextAppearance(textView, R.style.TextAppearance_Bold_Italic)
textView.setTextColor(context.getColorCompat(R.color.red_accent))
}
viewHolder.setVisibility(R.id.hot_desc, View.GONE)
viewHolder.itemView.background = getIntermixedColorBackground(
context,
position,
itemCount,
positionOffset = 1,
colors = intArrayOf(R.color.default_color_card, R.color.default_color_divider),
radius = context.resources.getDimension(R.dimen.card_radius)
)
}
override fun getItemLayoutId(): Int {
return R.layout.item_hot_message_list
}
init {
val navigationHelper = NavigationHelper.newInstance(context)
setOnItemClickListener { _, item, _ ->
navigationHelper.navigationByData(
NavigationHelper.ACTION_URL,
"https://tieba.baidu.com/mo/q/hotMessage?topic_id=${item.mulId}&topic_name=${item.mulName}"
)
}
}
}
override fun onTabSelected(tab: TabLayout.Tab) { override fun onTabSelected(tab: TabLayout.Tab) {
val fragment = fragmentAdapter.fragments[tab.position] val fragment = fragmentAdapter.fragments[tab.position]
if (fragment is Filterable) { if (fragment is Filterable) {

View File

@ -2,12 +2,11 @@ package com.huanchengfly.tieba.post.activities;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.huanchengfly.tieba.post.utils.TiebaUtil; import com.huanchengfly.tieba.post.utils.TiebaUtil;
import me.imid.swipebacklayout.lib.app.SwipeBackActivity; public class OKSignActivity extends AppCompatActivity {
public class OKSignActivity extends SwipeBackActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);

View File

@ -113,9 +113,8 @@ class ReplyActivity : BaseActivity(), View.OnClickListener,
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setSwipeBackEnable(false)
if (ThemeUtil.THEME_TRANSLUCENT == ThemeUtil.getTheme(this)) { if (ThemeUtil.THEME_TRANSLUCENT == ThemeUtil.getTheme(this)) {
val constraintLayout = findViewById(R.id.activity_reply_layout) as TintConstraintLayout val constraintLayout = findViewById<TintConstraintLayout>(R.id.activity_reply_layout)
constraintLayout.setBackgroundTintResId(0) constraintLayout.setBackgroundTintResId(0)
ThemeUtil.setTranslucentBackground(constraintLayout) ThemeUtil.setTranslucentBackground(constraintLayout)
} }
@ -278,8 +277,8 @@ class ReplyActivity : BaseActivity(), View.OnClickListener,
} }
}) })
mItemTouchHelper.attachToRecyclerView(insertView) mItemTouchHelper.attachToRecyclerView(insertView)
findViewById(R.id.activity_reply_root).setOnClickListener(this) findViewById<View>(R.id.activity_reply_root).setOnClickListener(this)
findViewById(R.id.activity_reply_layout).setOnClickListener(this) findViewById<View>(R.id.activity_reply_layout).setOnClickListener(this)
toolbar.setNavigationIcon(R.drawable.ic_reply_toolbar_round_close) toolbar.setNavigationIcon(R.drawable.ic_reply_toolbar_round_close)
if (replyInfoBean!!.pid == null && replyInfoBean!!.floorNum == null) { if (replyInfoBean!!.pid == null && replyInfoBean!!.floorNum == null) {
insertImageBtn.visibility = View.VISIBLE insertImageBtn.visibility = View.VISIBLE
@ -293,7 +292,7 @@ class ReplyActivity : BaseActivity(), View.OnClickListener,
if (replyInfoBean!!.replyUser != null) { if (replyInfoBean!!.replyUser != null) {
editText.hint = getString(R.string.hint_reply, replyInfoBean!!.replyUser) editText.hint = getString(R.string.hint_reply, replyInfoBean!!.replyUser)
} }
val tabLayout = findViewById(R.id.activity_reply_emotion_tab) as TabLayout val tabLayout = findViewById<TabLayout>(R.id.activity_reply_emotion_tab)
val emotionViewPagerAdapter = TabViewPagerAdapter() val emotionViewPagerAdapter = TabViewPagerAdapter()
val classicEmotionGridView = GridView(this) val classicEmotionGridView = GridView(this)
val emojiEmotionGridView = GridView(this) val emojiEmotionGridView = GridView(this)
@ -430,9 +429,9 @@ class ReplyActivity : BaseActivity(), View.OnClickListener,
} }
private fun initListener() { private fun initListener() {
val undo = findViewById(R.id.activity_reply_edit_undo) as TintImageView val undo = findViewById<TintImageView>(R.id.activity_reply_edit_undo)
val redo = findViewById(R.id.activity_reply_edit_redo) as TintImageView val redo = findViewById<TintImageView>(R.id.activity_reply_edit_redo)
val clear = findViewById(R.id.activity_reply_edit_clear) as TintImageView val clear = findViewById<TintImageView>(R.id.activity_reply_edit_clear)
undo.setOnClickListener(this) undo.setOnClickListener(this)
setEnabled(undo, false) setEnabled(undo, false)
redo.setOnClickListener(this) redo.setOnClickListener(this)

View File

@ -52,7 +52,6 @@ import com.huanchengfly.tieba.post.utils.preload.PreloadUtil
import com.huanchengfly.tieba.post.utils.preload.loaders.ThreadContentLoader import com.huanchengfly.tieba.post.utils.preload.loaders.ThreadContentLoader
import com.huanchengfly.tieba.post.widgets.VideoPlayerStandard import com.huanchengfly.tieba.post.widgets.VideoPlayerStandard
import com.scwang.smart.refresh.layout.SmartRefreshLayout import com.scwang.smart.refresh.layout.SmartRefreshLayout
import me.imid.swipebacklayout.lib.SwipeBackLayout.SwipeListener
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
@ -174,13 +173,6 @@ class ThreadActivity : BaseActivity(), View.OnClickListener, IThreadMenuFragment
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeUtil.setTranslucentThemeBackground(findViewById(R.id.background)) ThemeUtil.setTranslucentThemeBackground(findViewById(R.id.background))
swipeBackLayout.addSwipeListener(object : SwipeListener {
override fun onScrollStateChange(state: Int, scrollPercent: Float) {}
override fun onScrollOverThreshold() {}
override fun onEdgeTouch(edgeFlag: Int) {
exit()
}
})
refreshLayout.apply { refreshLayout.apply {
ThemeUtil.setThemeForSmartRefreshLayout(this) ThemeUtil.setThemeForSmartRefreshLayout(this)
refreshLayout.setOnRefreshListener { refreshLayout.setOnRefreshListener {
@ -243,8 +235,8 @@ class ThreadActivity : BaseActivity(), View.OnClickListener, IThreadMenuFragment
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.title = null supportActionBar?.title = null
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
findViewById(R.id.thread_bottom_bar_agree).setOnClickListener(this) findViewById<View>(R.id.thread_bottom_bar_agree).setOnClickListener(this)
findViewById(R.id.thread_reply_bar).setOnClickListener(this) findViewById<View>(R.id.thread_reply_bar).setOnClickListener(this)
moreBtn.setOnClickListener(this) moreBtn.setOnClickListener(this)
toolbar.setOnClickListener(this) toolbar.setOnClickListener(this)
threadHeaderAdapter.setOnToggleSeeLzListener { threadHeaderAdapter.setOnToggleSeeLzListener {

View File

@ -204,7 +204,7 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
private fun refreshBackground() { private fun refreshBackground() {
mProgress.visibility = View.VISIBLE mProgress.visibility = View.VISIBLE
if (mUri == null) { if (mUri == null) {
findViewById(R.id.background).setBackgroundColor(Color.BLACK) findViewById<View>(R.id.background).setBackgroundColor(Color.BLACK)
mProgress.visibility = View.GONE mProgress.visibility = View.GONE
return return
} }
@ -222,7 +222,8 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) { override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
resource.alpha = alpha resource.alpha = alpha
val bitmap = ImageUtil.drawableToBitmap(resource) val bitmap = ImageUtil.drawableToBitmap(resource)
findViewById(R.id.background).background = BitmapDrawable(resources, bitmap) findViewById<View>(R.id.background).background =
BitmapDrawable(resources, bitmap)
mPalette = Palette.from(bitmap).generate() mPalette = Palette.from(bitmap).generate()
mTranslucentThemeColorAdapter.setPalette(mPalette) mTranslucentThemeColorAdapter.setPalette(mPalette)
mSelectColor.visibility = View.VISIBLE mSelectColor.visibility = View.VISIBLE
@ -291,7 +292,7 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
appPreferences.translucentPrimaryColor = toString(themeColor) appPreferences.translucentPrimaryColor = toString(themeColor)
ThemeUtils.refreshUI(this) ThemeUtils.refreshUI(this)
} }
(findViewById(R.id.select_color_recycler_view) as RecyclerView).apply { (findViewById<RecyclerView>(R.id.select_color_recycler_view)).apply {
addItemDecoration(HorizontalSpacesDecoration(0, 0, 12.dpToPx(), 12.dpToPx(), false)) addItemDecoration(HorizontalSpacesDecoration(0, 0, 12.dpToPx(), 12.dpToPx(), false))
layoutManager = MyLinearLayoutManager( layoutManager = MyLinearLayoutManager(
this@TranslucentThemeActivity, this@TranslucentThemeActivity,
@ -302,11 +303,11 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
} }
alpha = appPreferences.translucentBackgroundAlpha alpha = appPreferences.translucentBackgroundAlpha
blur = appPreferences.translucentBackgroundBlur blur = appPreferences.translucentBackgroundBlur
(findViewById(R.id.alpha) as SeekBar).apply { (findViewById<SeekBar>(R.id.alpha)).apply {
progress = this@TranslucentThemeActivity.alpha progress = this@TranslucentThemeActivity.alpha
setOnSeekBarChangeListener(this@TranslucentThemeActivity) setOnSeekBarChangeListener(this@TranslucentThemeActivity)
} }
(findViewById(R.id.blur) as SeekBar).apply { (findViewById<SeekBar>(R.id.blur)).apply {
progress = this@TranslucentThemeActivity.blur progress = this@TranslucentThemeActivity.blur
setOnSeekBarChangeListener(this@TranslucentThemeActivity) setOnSeekBarChangeListener(this@TranslucentThemeActivity)
} }

View File

@ -0,0 +1,134 @@
package com.huanchengfly.tieba.post.ui.slideback;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import androidx.annotation.NonNull;
public class DefaultSlideView implements ISlideView {
private final int arrowWidth;
private final int width;
//private LinearGradient shader;
private final int height;
private Path bezierPath;
private Paint paint, arrowPaint;
private int backViewColor = 0xff000000;
private int arrowColor = Color.WHITE;
public DefaultSlideView(@NonNull Context context) {
width = Utils.d2p(context, 50);
height = Utils.d2p(context, 200);
arrowWidth = Utils.d2p(context, 4);
init(context);
}
public void setBackViewColor(int backViewColor) {
this.backViewColor = backViewColor;
}
public void setArrowColor(int arrowColor) {
this.arrowColor = arrowColor;
}
private void init(Context context) {
bezierPath = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(backViewColor);
paint.setStrokeWidth(Utils.d2p(context, 1.5f));
arrowPaint = new Paint();
arrowPaint.setAntiAlias(true);
arrowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
arrowPaint.setColor(arrowColor);
arrowPaint.setStrokeWidth(Utils.d2p(context, 1.5f));
arrowPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
public boolean scrollVertical() {
return true;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public void onDraw(Canvas canvas, float currentWidth, int orientation) {
float height = getHeight();
int maxWidth = getWidth();
float centerY = height / 2;
float progress = currentWidth / maxWidth;
if (progress == 0) {
return;
}
paint.setColor(backViewColor);
paint.setAlpha((int) (200 * progress));
//画半弧背景
/*
ps: 小点为起始点和结束点星号为控制点
·
|
*
*
|
·
|
*
*
|
·
*/
float bezierWidth = currentWidth / 2;
bezierPath.reset();
bezierPath.moveTo(0, 0);
bezierPath.cubicTo(0, height / 4f, bezierWidth, height * 3f / 8, bezierWidth, centerY);
bezierPath.cubicTo(bezierWidth, height * 5f / 8, 0, height * 3f / 4, 0, height);
canvas.drawPath(bezierPath, paint);
arrowPaint.setColor(arrowColor);
arrowPaint.setAlpha((int) (255 * progress));
//画箭头
float arrowStart, arrowEnd;
if (orientation == SlideBackView.ORIENTATION_RIGHT) {
arrowStart = currentWidth / 6;
arrowEnd = arrowStart + (arrowWidth * (progress - 0.7f) / 0.3f);
} else {
arrowStart = currentWidth / 6;
arrowEnd = arrowStart - (arrowWidth * (progress - 0.7f) / 0.3f);
}
if (progress <= 0.2) {
//ingore
} else if (progress <= 0.7f) {
//起初变长竖直过程
float newProgress = (progress - 0.2f) / 0.5f;
canvas.drawLine(arrowStart, centerY - arrowWidth * newProgress, arrowStart, centerY + arrowWidth * newProgress, arrowPaint);
} else {
//后面变形到完整箭头过程
canvas.drawLine(arrowEnd, centerY - arrowWidth, arrowStart, centerY, arrowPaint);
canvas.drawLine(arrowStart, centerY, arrowEnd, centerY + arrowWidth, arrowPaint);
}
}
}

View File

@ -0,0 +1,35 @@
package com.huanchengfly.tieba.post.ui.slideback;
import android.graphics.Canvas;
public interface ISlideView {
/**
* 是否可以垂直滑动
*
* @return
*/
boolean scrollVertical();
/**
* 宽度
*
* @return
*/
int getWidth();
/**
* 高度
*
* @return
*/
int getHeight();
/**
* 绘制
*
* @param canvas
* @param currentWidth 根据手指滑动得出的当前宽度最大值为getWidth())
* @param orientation 滑动方向
*/
void onDraw(Canvas canvas, float currentWidth, int orientation);
}

View File

@ -0,0 +1,5 @@
package com.huanchengfly.tieba.post.ui.slideback;
public interface OnSlide {
void onSlideBack();
}

View File

@ -0,0 +1,62 @@
package com.huanchengfly.tieba.post.ui.slideback;
import android.app.Activity;
import androidx.annotation.NonNull;
public class SlideBack {
private ISlideView slideView; //样式
private OnSlide onSlide; //滑动监听
private int canSlideWidth; //左边触发距离
public static SlideBack create() {
return new SlideBack();
}
/**
* 滑动返回样式
*
* @param slideView the slide view
* @return the slide back
*/
public SlideBack slideView(ISlideView slideView) {
this.slideView = slideView;
return this;
}
/**
* 左边开始触发距离
*
* @param canSlideWidth the can slide width
* @return the slide back
*/
public SlideBack canSlideWidth(int canSlideWidth) {
this.canSlideWidth = canSlideWidth;
return this;
}
/**
* 滑动触发监听
*
* @param onSlide the on slide
* @return the slide back
*/
public SlideBack onSlide(OnSlide onSlide) {
this.onSlide = onSlide;
return this;
}
public SlideControlLayout attachToActivity(@NonNull Activity activity) {
if (slideView == null) {
slideView = new DefaultSlideView(activity);
}
if (canSlideWidth == 0) {
canSlideWidth = Utils.d2p(activity, 18);
}
return new SlideControlLayout(activity, canSlideWidth, slideView, onSlide)
.attachToActivity(activity);
}
}

View File

@ -0,0 +1,96 @@
package com.huanchengfly.tieba.post.ui.slideback;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import androidx.annotation.NonNull;
@SuppressLint("ViewConstructor")
public class SlideBackView extends View {
public static final int ORIENTATION_LEFT = 0;
public static final int ORIENTATION_RIGHT = 1;
private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private ISlideView slideView;
private ValueAnimator animator;
private float rate = 0;//曲线的控制点
private int orientation = 0;//滑动方向
SlideBackView(Context context, @NonNull ISlideView slideView) {
super(context);
setSlideView(slideView);
}
public ISlideView getSlideView() {
return slideView;
}
public SlideBackView setSlideView(@NonNull ISlideView slideView) {
this.slideView = slideView;
setLayoutParams(new SlideControlLayout.LayoutParams(slideView.getWidth(), slideView.getHeight()));
return this;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
slideView.onDraw(canvas, rate, orientation);
}
public void updateOrientation(int orientation) {
this.orientation = orientation;
}
public void updateRate(float updateRate, boolean hasAnim) {
if (updateRate > getWidth()) {
updateRate = getWidth();
}
if (rate == updateRate) {
return;
}
cancelAnim();
if (!hasAnim) {
rate = updateRate;
invalidate();
if (rate == 0) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);
}
}
animator = ValueAnimator.ofFloat(rate, updateRate);
animator.setDuration(200);
animator.addUpdateListener(animation -> {
rate = (Float) animation.getAnimatedValue();
postInvalidate();
if (rate == 0) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);
}
});
animator.setInterpolator(DECELERATE_INTERPOLATOR);
animator.start();
}
private void cancelAnim() {
if (animator != null && animator.isRunning()) {
animator.cancel();
}
}
@Override
protected void onDetachedFromWindow() {
cancelAnim();
if (rate != 0) {
rate = 0;
invalidate();
}
super.onDetachedFromWindow();
}
}

View File

@ -0,0 +1,139 @@
package com.huanchengfly.tieba.post.ui.slideback;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@SuppressLint("ViewConstructor")
public class SlideControlLayout extends FrameLayout {
private final SlideBackView slideBackView;
private final OnSlide onSlide;
private final int canSlideWidth;
private final boolean enable = true;
private float downX;
private float moveX;
private boolean startDrag = false;
SlideControlLayout(@NonNull Context context, int canSlideWidth, ISlideView slideView, OnSlide onSlide) {
super(context);
this.canSlideWidth = canSlideWidth;
this.onSlide = onSlide;
slideBackView = new SlideBackView(context, slideView);
addView(slideBackView);
}
SlideControlLayout attachToActivity(@NonNull Activity activity) {
ViewParent parent = getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(this);
}
ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
decor.addView(this, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return this;
}
private void onBack() {
if (onSlide == null) {
Utils.getActivityContext(getContext()).onBackPressed();
} else {
onSlide.onSlideBack();
}
}
private void setSlideViewY(SlideBackView view, int y) {
if (!view.getSlideView().scrollVertical()) {
scrollTo(0, 0);
return;
}
scrollTo(0, -(y - view.getHeight() / 2));
}
//region 手势控制
@Override
public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
if (!enable) {
return false;
}
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
if (motionEvent.getRawX() <= canSlideWidth) {
return true;
}
if (motionEvent.getRawX() >= Utils.getScreenWidth(getContext()) - canSlideWidth) {
return true;
}
}
return super.onInterceptTouchEvent(motionEvent);
}
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
if (!enable) {
return super.onTouchEvent(motionEvent);
}
float currentX = motionEvent.getRawX();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
float currentY = motionEvent.getRawY();
if (currentY > Utils.d2p(getContext(), 100)) {
if (currentX <= canSlideWidth) {
setRotationY(0f);
downX = currentX;
startDrag = true;
slideBackView.updateRate(0, false);
slideBackView.updateOrientation(SlideBackView.ORIENTATION_RIGHT);
setSlideViewY(slideBackView, (int) (motionEvent.getRawY()));
} else if (currentX >= Utils.getScreenWidth(getContext()) - canSlideWidth) {
setRotationY(180f);
downX = currentX;
startDrag = true;
slideBackView.updateRate(0, false);
slideBackView.updateOrientation(SlideBackView.ORIENTATION_LEFT);
setSlideViewY(slideBackView, (int) (motionEvent.getRawY()));
}
}
break;
case MotionEvent.ACTION_MOVE:
if (startDrag) {
moveX = currentX - downX;
if (Math.abs(moveX) <= slideBackView.getWidth() * 2) {
slideBackView.updateRate(Math.abs(moveX) / 2, false);
} else {
slideBackView.updateRate(slideBackView.getWidth(), false);
}
setSlideViewY(slideBackView, (int) (motionEvent.getRawY()));
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
if (startDrag && Math.abs(moveX) >= slideBackView.getWidth() * 2) {
onBack();
slideBackView.updateRate(0, false);
} else {
slideBackView.updateRate(0, startDrag);
}
moveX = 0;
startDrag = false;
break;
}
return startDrag || super.onTouchEvent(motionEvent);
}
//endregion
}

View File

@ -0,0 +1,48 @@
package com.huanchengfly.tieba.post.ui.slideback;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Color;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.WindowManager;
import androidx.annotation.ColorInt;
public class Utils {
/**
* 屏幕宽度(像素)
*/
private static int screentwidth;
@ColorInt
static int setColorAlpha(int color, float alpha) {
color = Color.argb((int) (alpha * 255), Color.red(color), Color.green(color), Color.blue(color));
return color;
}
static int d2p(Context var0, float var1) {
DisplayMetrics var2 = var0.getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(1, var1, var2);
}
static int getScreenWidth(Context context) {
if (screentwidth > 0)
return screentwidth;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return screentwidth = outMetrics.widthPixels;
}
static Activity getActivityContext(Context context) {
if (context == null)
return null;
else if (context instanceof Activity)
return (Activity) context;
else if (context instanceof ContextWrapper)
return getActivityContext(((ContextWrapper) context).getBaseContext());
return null;
}
}