[1.0.0]
[视频播放]
This commit is contained in:
yangyakun
2023-02-12 11:59:08 +08:00
parent eee29a65eb
commit a907f2a96f
32 changed files with 2231 additions and 7 deletions

View File

@@ -76,6 +76,7 @@ dependencies {
implementation project(":OCH:mogo-och-common-module") implementation project(":OCH:mogo-och-common-module")
compileOnly project(":libraries:mogo-map") compileOnly project(":libraries:mogo-map")
compileOnly project(':libraries:mogo-adas') compileOnly project(':libraries:mogo-adas')
implementation project(':core:mogo-core-res')
} }

View File

@@ -0,0 +1,38 @@
package com.mogo.och.bus.passenger.bean;
public class TaxiPassengerVideoPlay {
public TaxiPassengerVideoPlay(String url, String imageUrl, String title) {
this.url = url;
this.imageUrl = imageUrl;
this.title = title;
}
private String url;
private String imageUrl;
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}

View File

@@ -1,9 +1,20 @@
package com.mogo.och.bus.passenger.ui package com.mogo.och.bus.passenger.ui
import android.os.Bundle import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.mogo.commons.mvp.MvpFragment import com.mogo.commons.mvp.MvpFragment
import com.mogo.och.bus.passenger.R import com.mogo.och.bus.passenger.R
import com.mogo.och.bus.passenger.bean.TaxiPassengerVideoPlay
import com.mogo.och.bus.passenger.presenter.BusPassengerFunctionVideoPresenter import com.mogo.och.bus.passenger.presenter.BusPassengerFunctionVideoPresenter
import com.mogo.och.bus.passenger.ui.adapter.RecyclerVideoAdapter
import com.mogo.och.bus.passenger.ui.layoutmanage.CarouselLayoutManager
import com.mogo.och.bus.passenger.ui.layoutmanage.CarouselZoomPostLayoutListener
import com.mogo.och.bus.passenger.ui.layoutmanage.CenterScrollListener
import com.mogo.och.bus.passenger.view.ConsultVideoPlayer
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
import kotlinx.android.synthetic.m1.bus_p_function_video_fragment.*
import kotlin.math.floor
/** /**
* @author: yangyakun * @author: yangyakun
@@ -11,6 +22,9 @@ import com.mogo.och.bus.passenger.presenter.BusPassengerFunctionVideoPresenter
*/ */
class BusPassengerFunctionVideoFragment : class BusPassengerFunctionVideoFragment :
MvpFragment<BusPassengerFunctionVideoFragment?, BusPassengerFunctionVideoPresenter?>() { MvpFragment<BusPassengerFunctionVideoFragment?, BusPassengerFunctionVideoPresenter?>() {
private val arrayListOf = ArrayList<TaxiPassengerVideoPlay>()
override fun getLayoutId(): Int { override fun getLayoutId(): Int {
return R.layout.bus_p_function_video_fragment return R.layout.bus_p_function_video_fragment
} }
@@ -20,7 +34,108 @@ class BusPassengerFunctionVideoFragment :
} }
override fun initViews() { override fun initViews() {
initConsultData()
val carouselLayoutManager = CarouselLayoutManager(CarouselLayoutManager.HORIZONTAL, true)
carouselLayoutManager.setPostLayoutListener(CarouselZoomPostLayoutListener())
carouselLayoutManager.maxVisibleItems = 1
rvVideoPlaylist.addOnScrollListener(object : CenterScrollListener() {
var prePlayerPosition = 0
override fun pageSelect(recyclerView: RecyclerView?, newState: Int) {
//播放视频
val (centerItemPosition: kotlin.Int, player) = getPlayer(carouselLayoutManager)
if (player is ConsultVideoPlayer) {
if (prePlayerPosition != centerItemPosition) {
if (player.currentState == GSYVideoView.CURRENT_STATE_PAUSE) {
player.onVideoReset()
}
val playerHolder =
carouselLayoutManager.findViewByPosition(prePlayerPosition)
val prePlayer =
playerHolder?.findViewById<ConsultVideoPlayer>(R.id.video_item_player)
prePlayer?.onVideoReset()
val taxiPassengerVideoPlay = arrayListOf[centerItemPosition]
setBackageAndPlayNext(taxiPassengerVideoPlay)
} else {
player.onVideoResume(false)
}
}
prePlayerPosition = centerItemPosition
}
override fun pageStop() {
val (_: kotlin.Int, player) = getPlayer(carouselLayoutManager)
if (player is ConsultVideoPlayer) {
player.onVideoPause()
}
}
})
carouselLayoutManager.addOnDargAutoDiffListener { adapterPosition, currentPosition ->
val fl = adapterPosition - floor(adapterPosition)
var currentIndex = currentPosition
if (fl > 0.5) {
if (currentPosition == 0) {
currentIndex = rvVideoPlaylist?.adapter!!.itemCount - 1
} else {
currentIndex -= 1
}
}
}
val recyclerVideoAdapter = RecyclerVideoAdapter(requireContext(), arrayListOf, rvVideoPlaylist)
recyclerVideoAdapter.setOnThumbImageClilckListener {
val (_: kotlin.Int, player) = getPlayer(carouselLayoutManager)
if (player is ConsultVideoPlayer) {
player.onVideoReset()
player.thumbImageViewLayout.visibility = View.VISIBLE
}
rvVideoPlaylist?.smoothScrollToPosition(it)
}
rvVideoPlaylist?.layoutManager = carouselLayoutManager
rvVideoPlaylist?.setHasFixedSize(true)
rvVideoPlaylist?.adapter = recyclerVideoAdapter
}
private fun getPlayer(carouselLayoutManager: CarouselLayoutManager): Pair<Int, ConsultVideoPlayer?> {
val centerItemPosition: Int = carouselLayoutManager.centerItemPosition
val playerHolder = carouselLayoutManager.findViewByPosition(centerItemPosition)
val player = playerHolder?.findViewById<ConsultVideoPlayer>(R.id.video_item_player)
return Pair(centerItemPosition, player)
}
private fun setBackageAndPlayNext(taxiPassengerVideoPlay: TaxiPassengerVideoPlay) {
// 设置背景图片
}
private fun initConsultData() {
arrayListOf.clear()
arrayListOf.add(
TaxiPassengerVideoPlay(
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708596763/全车型混剪增加红旗车队.m4v",
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969511280/车队.png",
"蘑菇车联覆盖生活的方方面面"
)
)
arrayListOf.add(
TaxiPassengerVideoPlay(
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708554279/红旗车队.m4v",
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969553174/红旗重新排版.png",
"蘑菇车联之红旗车队"
)
)
arrayListOf.add(
TaxiPassengerVideoPlay(
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708499497/大运会合作解说版.m4v",
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969536177/大运会.png",
"蘑菇车联牵手成都大运会"
)
)
arrayListOf.add(
TaxiPassengerVideoPlay(
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708409810/20210610重新排版3屏.m4v",
"https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969579713/三屏.png",
"多视角体验蘑菇车联自动驾驶"
)
)
} }
override fun createPresenter(): BusPassengerFunctionVideoPresenter { override fun createPresenter(): BusPassengerFunctionVideoPresenter {

View File

@@ -0,0 +1,29 @@
package com.mogo.och.bus.passenger.ui.adapter;
import android.content.Context;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import com.mogo.och.bus.passenger.R;
import com.mogo.och.bus.passenger.view.ConsultVideoPlayer;
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder;
public class RecyclerItemVideoHolder extends RecyclerView.ViewHolder {
public final static String TAG = "RecyclerView2List";
protected Context context;
public ConsultVideoPlayer gsyVideoPlayer;
GSYVideoOptionBuilder gsyVideoOptionBuilder;
public RecyclerItemVideoHolder(Context context, View v) {
super(v);
this.context = context;
gsyVideoPlayer = v.findViewById(R.id.video_item_player);
gsyVideoOptionBuilder = new GSYVideoOptionBuilder();
}
}

View File

@@ -0,0 +1,130 @@
package com.mogo.och.bus.passenger.ui.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.mogo.eagle.core.utilcode.util.ToastUtils;
import com.mogo.och.bus.passenger.R;
import com.mogo.och.bus.passenger.bean.TaxiPassengerVideoPlay;
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack;
import java.util.List;
import me.jessyan.autosize.AutoSizeCompat;
public class RecyclerVideoAdapter extends RecyclerView.Adapter<RecyclerItemVideoHolder> {
private final static String TAG = "RecyclerBaseAdapter";
private List<TaxiPassengerVideoPlay> itemDataList = null;
private Context context = null;
private OnThumbImageClilckListener onThumbImageClilckListener;
private RecyclerView recyclerView;
public OnThumbImageClilckListener getOnThumbImageClilckListener() {
return onThumbImageClilckListener;
}
public void setOnThumbImageClilckListener(OnThumbImageClilckListener onThumbImageClilckListener) {
this.onThumbImageClilckListener = onThumbImageClilckListener;
}
public RecyclerVideoAdapter(Context context, List<TaxiPassengerVideoPlay> itemDataList,RecyclerView recyclerView) {
this.itemDataList = itemDataList;
this.context = context;
this.recyclerView = recyclerView;
}
public TaxiPassengerVideoPlay getItemByPosition(int position){
if(itemDataList!=null){
return itemDataList.get(position);
}
return null;
}
@NonNull
@Override
public RecyclerItemVideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.list_video_item_normal, parent, false);
RecyclerItemVideoHolder recyclerItemVideoHolder = new RecyclerItemVideoHolder(context, v);
recyclerItemVideoHolder.setIsRecyclable(false);
return recyclerItemVideoHolder;
}
@Override
public void onBindViewHolder(@NonNull final RecyclerItemVideoHolder holder, int position) {
final TaxiPassengerVideoPlay taxiPassengerVideoPlay = itemDataList.get(position);
AutoSizeCompat.autoConvertDensityOfGlobal(holder.itemView.getResources());
holder.gsyVideoOptionBuilder
.setEnlargeImageRes(R.drawable.taxi_p_change_full)
.setUrl(taxiPassengerVideoPlay.getUrl())
.setCacheWithPlay(true)
.setPlayTag(taxiPassengerVideoPlay.getImageUrl()+position)
.setThumbPlay(false)
.build(holder.gsyVideoPlayer);
holder.gsyVideoPlayer.getTitleTextView().setText(taxiPassengerVideoPlay.getTitle());
Glide.with(context)
.load(taxiPassengerVideoPlay.getImageUrl())
.apply(new RequestOptions().placeholder(R.drawable.taxi_p_video_holder).centerCrop())
.into(holder.gsyVideoPlayer.coverImage);
holder.gsyVideoPlayer.getThumbImageViewLayout().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(onThumbImageClilckListener!=null){
onThumbImageClilckListener.onDxChanged(holder.getAbsoluteAdapterPosition());
}
}
});
holder.gsyVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack(){
@Override
public void onAutoComplete(String url, Object... objects) {
holder.gsyVideoPlayer.onVideoReset();
if(holder.getAbsoluteAdapterPosition()==getItemCount()-1){
recyclerView.smoothScrollToPosition(0);
}else {
recyclerView.smoothScrollToPosition(holder.getAbsoluteAdapterPosition()+1);
}
}
@Override
public void onClickBlank(String url, Object... objects) {
super.onClickBlank(url, objects);
recyclerView.smoothScrollToPosition(holder.getAbsoluteAdapterPosition());
}
@Override
public void onPlayError(String url, Object... objects) {
ToastUtils.showLong("哎呀,出错了,看看其他视频吧");
}
@Override
public void onClickStartError(String url, Object... objects) {
ToastUtils.showLong("哎呀,出错了,看看其他视频吧");
}
});
}
@Override
public int getItemCount() {
return itemDataList.size();
}
@Override
public int getItemViewType(int position) {
return 1;
}
public interface OnThumbImageClilckListener {
void onDxChanged(int targetPosition);
}
}

View File

@@ -0,0 +1,970 @@
package com.mogo.och.bus.passenger.ui.layoutmanage;
import android.graphics.PointF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.OrientationHelper;
import androidx.recyclerview.widget.RecyclerView;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* An implementation of {@link RecyclerView.LayoutManager} that layout items like carousel.
* Generally there is one center item and bellow this item there are maximum {@link CarouselLayoutManager#getMaxVisibleItems()} items on each side of the center
* item. By default {@link CarouselLayoutManager#getMaxVisibleItems()} is {@link CarouselLayoutManager#MAX_VISIBLE_ITEMS}.<br />
* <br />
* This LayoutManager supports only fixedSized adapter items.<br />
* <br />
* This LayoutManager supports {@link CarouselLayoutManager#HORIZONTAL} and {@link CarouselLayoutManager#VERTICAL} orientations. <br />
* <br />
* This LayoutManager supports circle layout. By default it if disabled. We don't recommend to use circle layout with adapter items count less then 3. <br />
* <br />
* Please be sure that layout_width of adapter item is a constant value and not {@link ViewGroup.LayoutParams#MATCH_PARENT}
* for {@link #HORIZONTAL} orientation.
* So like layout_height is not {@link ViewGroup.LayoutParams#MATCH_PARENT} for {@link CarouselLayoutManager#VERTICAL}<br />
* <br />
*/
public class CarouselLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider {
public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
public static final int VERTICAL = OrientationHelper.VERTICAL;
/**
* 固定值一直不变
*/
public static final int INVALID_POSITION = -1;
public static final int MAX_VISIBLE_ITEMS = 3;
private static final boolean CIRCLE_LAYOUT = false;
private boolean mDecoratedChildSizeInvalid;
private Integer mDecoratedChildWidth;
private Integer mDecoratedChildHeight;
private final int mOrientation;
private boolean mCircleLayout;
private int mPendingScrollPosition;
private final LayoutHelper mLayoutHelper = new LayoutHelper(MAX_VISIBLE_ITEMS);
private PostLayoutListener mViewPostLayout;
private final List<OnCenterItemSelectionListener> mOnCenterItemSelectionListeners = new ArrayList<>();
private final List<OnDargAutoDiffListener> onDargAutoDiffListeners = new ArrayList<>();
private int mCenterItemPosition = INVALID_POSITION;
private int mItemsCount;
@Nullable
private CarouselSavedState mPendingCarouselSavedState;
/**
* @param orientation should be {@link #VERTICAL} or {@link #HORIZONTAL}
*/
@SuppressWarnings("unused")
public CarouselLayoutManager(final int orientation) {
this(orientation, CIRCLE_LAYOUT);
}
/**
* If circleLayout is true then all items will be in cycle. Scroll will be infinite on both sides.
*
* @param orientation should be {@link #VERTICAL} or {@link #HORIZONTAL}
* @param circleLayout true for enabling circleLayout
*/
@SuppressWarnings("unused")
public CarouselLayoutManager(final int orientation, final boolean circleLayout) {
if (HORIZONTAL != orientation && VERTICAL != orientation) {
throw new IllegalArgumentException("orientation should be HORIZONTAL or VERTICAL");
}
mOrientation = orientation;
mCircleLayout = circleLayout;
mPendingScrollPosition = INVALID_POSITION;
}
/**
* Change circle layout type
*/
@SuppressWarnings("unused")
public void setCircleLayout(final boolean circleLayout) {
if (mCircleLayout != circleLayout) {
mCircleLayout = circleLayout;
requestLayout();
}
}
/**
* Setup {@link PostLayoutListener} for this LayoutManager.
* Its methods will be called for each visible view item after general LayoutManager layout finishes. <br />
* <br />
* Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting.
*
* @param postLayoutListener listener for item layout changes. Can be null.
*/
@SuppressWarnings("unused")
public void setPostLayoutListener(@Nullable final PostLayoutListener postLayoutListener) {
mViewPostLayout = postLayoutListener;
requestLayout();
}
/**
* Setup maximum visible (layout) items on each side of the center item.
* Basically during scrolling there can be more visible items (+1 item on each side), but in idle state this is the only reached maximum.
*
* @param maxVisibleItems should be great then 0, if bot an {@link IllegalAccessException} will be thrown
*/
@CallSuper
@SuppressWarnings("unused")
public void setMaxVisibleItems(final int maxVisibleItems) {
if (0 > maxVisibleItems) {
throw new IllegalArgumentException("maxVisibleItems can't be less then 0");
}
mLayoutHelper.mMaxVisibleItems = maxVisibleItems;
requestLayout();
}
/**
* @return current setup for maximum visible items.
* @see #setMaxVisibleItems(int)
*/
@SuppressWarnings("unused")
public int getMaxVisibleItems() {
return mLayoutHelper.mMaxVisibleItems;
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
/**
* @return current layout orientation
* @see #VERTICAL
* @see #HORIZONTAL
*/
public int getOrientation() {
return mOrientation;
}
@Override
public boolean canScrollHorizontally() {
return 0 != getChildCount() && HORIZONTAL == mOrientation;
}
@Override
public boolean canScrollVertically() {
return 0 != getChildCount() && VERTICAL == mOrientation;
}
/**
* @return current layout center item
*/
public int getCenterItemPosition() {
return mCenterItemPosition;
}
/**
* @param onCenterItemSelectionListener listener that will trigger when ItemSelectionChanges. can't be null
*/
public void addOnItemSelectionListener(@NonNull final OnCenterItemSelectionListener onCenterItemSelectionListener) {
mOnCenterItemSelectionListeners.add(onCenterItemSelectionListener);
}
/**
* @param onCenterItemSelectionListener listener that was previously added by {@link #addOnItemSelectionListener(OnCenterItemSelectionListener)}
*/
public void removeOnItemSelectionListener(@NonNull final OnCenterItemSelectionListener onCenterItemSelectionListener) {
mOnCenterItemSelectionListeners.remove(onCenterItemSelectionListener);
}
public void addOnDargAutoDiffListener(@NonNull final OnDargAutoDiffListener onDargAutoDiffListener) {
onDargAutoDiffListeners.add(onDargAutoDiffListener);
}
public void removeOnDargAutoDiffListener(@NonNull final OnDargAutoDiffListener onDargAutoDiffListener) {
onDargAutoDiffListeners.remove(onDargAutoDiffListener);
}
@SuppressWarnings("RefusedBequest")
@Override
public void scrollToPosition(final int position) {
if (0 > position) {
throw new IllegalArgumentException("position can't be less then 0. position is : " + position);
}
mPendingScrollPosition = position;
requestLayout();
}
@SuppressWarnings("RefusedBequest")
@Override
public void smoothScrollToPosition(@NonNull final RecyclerView recyclerView, @NonNull final RecyclerView.State state, final int position) {
final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
@Override
public int calculateDyToMakeVisible(final View view, final int snapPreference) {
if (!canScrollVertically()) {
return 0;
}
return getOffsetForCurrentView(view);
}
@Override
public int calculateDxToMakeVisible(final View view, final int snapPreference) {
if (!canScrollHorizontally()) {
return 0;
}
return getOffsetForCurrentView(view);
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
@Override
@Nullable
public PointF computeScrollVectorForPosition(final int targetPosition) {
if (0 == getChildCount()) {
return null;
}
final float directionDistance = getScrollDirection(targetPosition);
//noinspection NumericCastThatLosesPrecision
final int direction = (int) -Math.signum(directionDistance);
if (HORIZONTAL == mOrientation) {
return new PointF(direction, 0);
} else {
return new PointF(0, direction);
}
}
private float getScrollDirection(final int targetPosition) {
final float currentScrollPosition = makeScrollPositionInRange0ToCount(getCurrentScrollPosition(), mItemsCount);
if (mCircleLayout) {
final float t1 = currentScrollPosition - targetPosition;
final float t2 = Math.abs(t1) - mItemsCount;
if (Math.abs(t1) > Math.abs(t2)) {
return Math.signum(t1) * t2;
} else {
return t1;
}
} else {
return currentScrollPosition - targetPosition;
}
}
@Override
public int scrollVerticallyBy(final int dy, @NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
if (HORIZONTAL == mOrientation) {
return 0;
}
return scrollBy(dy, recycler, state);
}
@Override
public int scrollHorizontallyBy(final int dx, final RecyclerView.Recycler recycler, final RecyclerView.State state) {
if (VERTICAL == mOrientation) {
return 0;
}
return scrollBy(dx, recycler, state);
}
/**
* This method is called from {@link #scrollHorizontallyBy(int, RecyclerView.Recycler, RecyclerView.State)} and
* {@link #scrollVerticallyBy(int, RecyclerView.Recycler, RecyclerView.State)} to calculate needed scroll that is allowed. <br />
* <br />
* This method may do relayout work.
*
* @param diff 要滚动的距离
* @param recycler 回收期
* @param state Transient state of RecyclerView
* @return distance that we actually scrolled by
*/
@CallSuper
protected int scrollBy(final int diff, @NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
if (null == mDecoratedChildWidth || null == mDecoratedChildHeight) {
return 0;
}
if (0 == getChildCount() || 0 == diff) {
return 0;
}
final int resultScroll;
if (mCircleLayout) {
resultScroll = diff;
mLayoutHelper.mScrollOffset += resultScroll;
final int maxOffset = getScrollItemSize() * mItemsCount;
while (0 > mLayoutHelper.mScrollOffset) {
mLayoutHelper.mScrollOffset += maxOffset;
}
while (mLayoutHelper.mScrollOffset > maxOffset) {
mLayoutHelper.mScrollOffset -= maxOffset;
}
mLayoutHelper.mScrollOffset -= resultScroll;
} else {
final int maxOffset = getMaxScrollOffset();
if (0 > mLayoutHelper.mScrollOffset + diff) {
resultScroll = -mLayoutHelper.mScrollOffset; //to make it 0
} else if (mLayoutHelper.mScrollOffset + diff > maxOffset) {
resultScroll = maxOffset - mLayoutHelper.mScrollOffset; //to make it maxOffset
} else {
resultScroll = diff;
}
}
if (0 != resultScroll) {
mLayoutHelper.mScrollOffset += resultScroll;
fillData(recycler, state);
}
return resultScroll;
}
@Override
public void onMeasure(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state, final int widthSpec, final int heightSpec) {
mDecoratedChildSizeInvalid = true;
super.onMeasure(recycler, state, widthSpec, heightSpec);
}
@SuppressWarnings("rawtypes")
@Override
public void onAdapterChanged(final RecyclerView.Adapter oldAdapter, final RecyclerView.Adapter newAdapter) {
super.onAdapterChanged(oldAdapter, newAdapter);
removeAllViews();
}
@SuppressWarnings("RefusedBequest")
@Override
@CallSuper
public void onLayoutChildren(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
if (0 == state.getItemCount()) {
removeAndRecycleAllViews(recycler);
selectItemCenterPosition(INVALID_POSITION);
return;
}
detachAndScrapAttachedViews(recycler);
if (null == mDecoratedChildWidth || mDecoratedChildSizeInvalid) {
final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
final boolean shouldRecycle;
final View view;
if (scrapList.isEmpty()) {
shouldRecycle = true;
final int itemsCount = state.getItemCount();
view = recycler.getViewForPosition(
mPendingScrollPosition == INVALID_POSITION ?
0 :
Math.max(0, Math.min(itemsCount - 1, mPendingScrollPosition))
);
addView(view);
} else {
shouldRecycle = false;
view = scrapList.get(0).itemView;
}
measureChildWithMargins(view, 0, 0);
final int decoratedChildWidth = getDecoratedMeasuredWidth(view);
final int decoratedChildHeight = getDecoratedMeasuredHeight(view);
if (shouldRecycle) {
detachAndScrapView(view, recycler);
}
if (null != mDecoratedChildWidth && (mDecoratedChildWidth != decoratedChildWidth || mDecoratedChildHeight != decoratedChildHeight)) {
if (INVALID_POSITION == mPendingScrollPosition && null == mPendingCarouselSavedState) {
mPendingScrollPosition = mCenterItemPosition;
}
}
mDecoratedChildWidth = decoratedChildWidth;
mDecoratedChildHeight = decoratedChildHeight;
mDecoratedChildSizeInvalid = false;
}
if (INVALID_POSITION != mPendingScrollPosition) {
final int itemsCount = state.getItemCount();
mPendingScrollPosition = 0 == itemsCount ? INVALID_POSITION : Math.max(0, Math.min(itemsCount - 1, mPendingScrollPosition));
}
if (INVALID_POSITION != mPendingScrollPosition) {
mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mPendingScrollPosition, state);
mPendingScrollPosition = INVALID_POSITION;
mPendingCarouselSavedState = null;
} else if (null != mPendingCarouselSavedState) {
mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mPendingCarouselSavedState.mCenterItemPosition, state);
mPendingCarouselSavedState = null;
} else if (state.didStructureChange() && INVALID_POSITION != mCenterItemPosition) {
mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mCenterItemPosition, state);
}
fillData(recycler, state);
}
private int calculateScrollForSelectingPosition(final int itemPosition, final RecyclerView.State state) {
if (itemPosition == INVALID_POSITION) {
return 0;
}
final int fixedItemPosition = itemPosition < state.getItemCount() ? itemPosition : state.getItemCount() - 1;
return fixedItemPosition * (VERTICAL == mOrientation ? mDecoratedChildHeight : mDecoratedChildWidth);
}
private void fillData(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
final float currentScrollPosition = getCurrentScrollPosition();
generateLayoutOrder(currentScrollPosition, state);
detachAndScrapAttachedViews(recycler);
recyclerOldViews(recycler);
final int width = getWidthNoPadding();
final int height = getHeightNoPadding();
if (VERTICAL == mOrientation) {
fillDataVertical(recycler, width, height);
} else {
fillDataHorizontal(recycler, width, height);
}
recycler.clear();
detectOnItemSelectionChanged(currentScrollPosition, state);
}
private void detectOnItemSelectionChanged(final float currentScrollPosition, final RecyclerView.State state) {
final float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, state.getItemCount());
final int centerItem = Math.round(absCurrentScrollPosition);
if(currentScrollPosition-centerItem!=0){
new Handler(Looper.getMainLooper()).post(() -> dragDxDiff(currentScrollPosition,mCenterItemPosition));
}
if (mCenterItemPosition != centerItem) {
mCenterItemPosition = centerItem;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
selectItemCenterPosition(centerItem);
}
});
}
}
private void selectItemCenterPosition(final int centerItem) {
for (final OnCenterItemSelectionListener onCenterItemSelectionListener : mOnCenterItemSelectionListeners) {
onCenterItemSelectionListener.onCenterItemChanged(centerItem);
}
}
private void dragDxDiff(final float centerItem,final int currentPosition) {
for (final OnDargAutoDiffListener onDargAutoDiffListener : onDargAutoDiffListeners) {
onDargAutoDiffListener.onDxChanged(centerItem,currentPosition);
}
}
private void fillDataVertical(final RecyclerView.Recycler recycler, final int width, final int height) {
final int start = (width - mDecoratedChildWidth) / 2;
final int end = start + mDecoratedChildWidth;
final int centerViewTop = (height - mDecoratedChildHeight) / 2;
for (int i = 0, count = mLayoutHelper.mLayoutOrder.length; i < count; ++i) {
final LayoutOrder layoutOrder = mLayoutHelper.mLayoutOrder[i];
final int offset = getCardOffsetByPositionDiff(layoutOrder.mItemPositionDiff);
final int top = centerViewTop + offset;
final int bottom = top + mDecoratedChildHeight;
fillChildItem(start, top, end, bottom, layoutOrder, recycler, i);
}
}
private void fillDataHorizontal(final RecyclerView.Recycler recycler, final int width, final int height) {
final int top = (height - mDecoratedChildHeight) / 2;
final int bottom = top + mDecoratedChildHeight;
final int centerViewStart = (width - mDecoratedChildWidth) / 2;
for (int i = 0, count = mLayoutHelper.mLayoutOrder.length; i < count; ++i) {
final LayoutOrder layoutOrder = mLayoutHelper.mLayoutOrder[i];
final int offset = getCardOffsetByPositionDiff(layoutOrder.mItemPositionDiff);
final int start = centerViewStart + offset;
final int end = start + mDecoratedChildWidth;
fillChildItem(start, top, end, bottom, layoutOrder, recycler, i);
}
}
@SuppressWarnings("MethodWithTooManyParameters")
private void fillChildItem(final int start, final int top, final int end, final int bottom, @NonNull final LayoutOrder layoutOrder, @NonNull final RecyclerView.Recycler recycler, final int i) {
final View view = bindChild(layoutOrder.mItemAdapterPosition, recycler);
ViewCompat.setElevation(view, i);
ItemTransformation transformation = null;
if (null != mViewPostLayout) {
transformation = mViewPostLayout.transformChild(view, layoutOrder.mItemPositionDiff, mOrientation, layoutOrder.mItemAdapterPosition);
}
if (null == transformation) {
view.layout(start, top, end, bottom);
} else {
view.layout(Math.round(start + transformation.mTranslationX), Math.round(top + transformation.mTranslationY),
Math.round(end + transformation.mTranslationX), Math.round(bottom + transformation.mTranslationY));
view.setScaleX(transformation.mScaleX);
view.setScaleY(transformation.mScaleY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
view.setTransitionAlpha(transformation.mAlpha);
}else {
view.setAlpha(transformation.mAlpha);
}
}
}
/**
* 中心项目的当前滚动位置。如果是循环布局,则该值可以在任何范围内。如果不是,那么它在[0-1]
*/
private float getCurrentScrollPosition() {
final int fullScrollSize = getMaxScrollOffset();
if (0 == fullScrollSize) {
return 0;
}
return 1.0f * mLayoutHelper.mScrollOffset / getScrollItemSize();
}
/**
* 填充布局中所有项目的最大滚动值。通常,这仅适用于非循环布局。
*/
private int getMaxScrollOffset() {
return getScrollItemSize() * (mItemsCount - 1);
}
/**
* Because we can support old Android versions, we should layout our children in specific order to make our center view in the top of layout
* (this item should layout last). So this method will calculate layout order and fill up {@link #mLayoutHelper} object.
* This object will be filled by only needed to layout items. Non visible items will not be there.
*
* @param currentScrollPosition current scroll position this is a value that indicates position of center item
* (if this value is int, then center item is really in the center of the layout, else it is near state).
* Be aware that this value can be in any range is it is cycle layout
* @param state Transient state of RecyclerView
* @see #getCurrentScrollPosition()
*/
private void generateLayoutOrder(final float currentScrollPosition, @NonNull final RecyclerView.State state) {
mItemsCount = state.getItemCount();
final float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, mItemsCount);
final int centerItem = Math.round(absCurrentScrollPosition);
if (mCircleLayout && 1 < mItemsCount) {
final int layoutCount = Math.min(mLayoutHelper.mMaxVisibleItems * 2 + 1, mItemsCount);
mLayoutHelper.initLayoutOrder(layoutCount);
final int countLayoutHalf = layoutCount / 2;
// before center item
for (int i = 1; i <= countLayoutHalf; ++i) {
final int position = Math.round(absCurrentScrollPosition - i + mItemsCount) % mItemsCount;
mLayoutHelper.setLayoutOrder(countLayoutHalf - i, position, centerItem - absCurrentScrollPosition - i);
}
// after center item
for (int i = layoutCount - 1; i >= countLayoutHalf + 1; --i) {
final int position = Math.round(absCurrentScrollPosition - i + layoutCount) % mItemsCount;
mLayoutHelper.setLayoutOrder(i - 1, position, centerItem - absCurrentScrollPosition + layoutCount - i);
}
mLayoutHelper.setLayoutOrder(layoutCount - 1, centerItem, centerItem - absCurrentScrollPosition);
} else {
final int firstVisible = Math.max(centerItem - mLayoutHelper.mMaxVisibleItems, 0);
final int lastVisible = Math.min(centerItem + mLayoutHelper.mMaxVisibleItems, mItemsCount - 1);
final int layoutCount = lastVisible - firstVisible + 1;
mLayoutHelper.initLayoutOrder(layoutCount);
for (int i = firstVisible; i <= lastVisible; ++i) {
if (i == centerItem) {
mLayoutHelper.setLayoutOrder(layoutCount - 1, i, i - absCurrentScrollPosition);
} else if (i < centerItem) {
mLayoutHelper.setLayoutOrder(i - firstVisible, i, i - absCurrentScrollPosition);
} else {
mLayoutHelper.setLayoutOrder(layoutCount - (i - centerItem) - 1, i, i - absCurrentScrollPosition);
}
}
}
}
public int getWidthNoPadding() {
return getWidth() - getPaddingStart() - getPaddingEnd();
}
public int getHeightNoPadding() {
return getHeight() - getPaddingEnd() - getPaddingStart();
}
private View bindChild(final int position, @NonNull final RecyclerView.Recycler recycler) {
final View view = recycler.getViewForPosition(position);
addView(view);
measureChildWithMargins(view, 0, 0);
return view;
}
private void recyclerOldViews(final RecyclerView.Recycler recycler) {
for (RecyclerView.ViewHolder viewHolder : new ArrayList<>(recycler.getScrapList())) {
int adapterPosition = viewHolder.getAdapterPosition();
boolean found = false;
for (LayoutOrder layoutOrder : mLayoutHelper.mLayoutOrder) {
if (layoutOrder.mItemAdapterPosition == adapterPosition) {
found = true;
break;
}
}
if (!found) {
recycler.recycleView(viewHolder.itemView);
}
}
}
/**
* Called during {@link #fillData(RecyclerView.Recycler, RecyclerView.State)} to calculate item offset from layout center line. <br />
* <br />
* Returns {@link #convertItemPositionDiffToSmoothPositionDiff(float)} * (size off area above center item when it is on the center). <br />
* Sign is: plus if this item is bellow center line, minus if not<br />
* <br />
* ----- - area above it<br />
* ||||| - center item<br />
* ----- - area bellow it (it has the same size as are above center item)<br />
*
* @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line.
* if this is 1 then this item is bellow the layout center line in the full item size distance.
* @return offset in scroll px coordinates.
*/
protected int getCardOffsetByPositionDiff(final float itemPositionDiff) {
final double smoothPosition = convertItemPositionDiffToSmoothPositionDiff(itemPositionDiff);
final int dimenDiff;
if (VERTICAL == mOrientation) {
dimenDiff = (getHeightNoPadding() - mDecoratedChildHeight) / 2;
} else {
dimenDiff = (getWidthNoPadding() - mDecoratedChildWidth) / 2;
}
//noinspection NumericCastThatLosesPrecision
return (int) Math.round(Math.signum(itemPositionDiff) * dimenDiff * smoothPosition);
}
/**
* Called during {@link #getCardOffsetByPositionDiff(float)} for better item movement. <br/>
* Current implementation speed up items that are far from layout center line and slow down items that are close to this line.
* This code is full of maths. If you want to make items move in a different way, probably you should override this method.<br />
* Please see code comments for better explanations.
*
* @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line.
* if this is 1 then this item is bellow the layout center line in the full item size distance.
* @return smooth position offset. needed for scroll calculation and better user experience.
* @see #getCardOffsetByPositionDiff(float)
*/
@SuppressWarnings({"MagicNumber", "InstanceMethodNamingConvention"})
protected double convertItemPositionDiffToSmoothPositionDiff(final float itemPositionDiff) {
// generally item moves the same way above center and bellow it. So we don't care about diff sign.
final float absIemPositionDiff = Math.abs(itemPositionDiff);
// we detect if this item is close for center or not. We use (1 / maxVisibleItem) ^ (1/3) as close definer.
if (absIemPositionDiff > StrictMath.pow(1.0f / mLayoutHelper.mMaxVisibleItems, 1.0f / 3)) {
// this item is far from center line, so we should make it move like square root function
return StrictMath.pow(absIemPositionDiff / mLayoutHelper.mMaxVisibleItems, 1 / 2.0f);
} else {
// this item is close from center line. we should slow it down and don't make it speed up very quick.
// so square function in range of [0, (1/maxVisible)^(1/3)] is quite good in it;
return StrictMath.pow(absIemPositionDiff, 2.0f);
}
}
/**
* @return full item size
*/
protected int getScrollItemSize() {
if (VERTICAL == mOrientation) {
return mDecoratedChildHeight;
} else {
return mDecoratedChildWidth;
}
}
@Override
public Parcelable onSaveInstanceState() {
if (null != mPendingCarouselSavedState) {
return new CarouselSavedState(mPendingCarouselSavedState);
}
final CarouselSavedState savedState = new CarouselSavedState(super.onSaveInstanceState());
savedState.mCenterItemPosition = mCenterItemPosition;
return savedState;
}
@Override
public void onRestoreInstanceState(final Parcelable state) {
if (state instanceof CarouselSavedState) {
mPendingCarouselSavedState = (CarouselSavedState) state;
super.onRestoreInstanceState(mPendingCarouselSavedState.mSuperState);
} else {
super.onRestoreInstanceState(state);
}
}
/**
* @return 从中心到最近项目的滚动偏移量
*/
protected int getOffsetCenterView() {
return Math.round(getCurrentScrollPosition()) * getScrollItemSize() - mLayoutHelper.mScrollOffset;
}
protected int getOffsetForCurrentView(@NonNull final View view) {
final int targetPosition = getPosition(view);
final float directionDistance = getScrollDirection(targetPosition);
return Math.round(directionDistance * getScrollItemSize());
}
/**
* 使滚动范围在[0count]内的Helper方法。通常只有循环布局才需要此方法。
*
* @param currentScrollPosition 滚动位置范围 个位数 view的index 小数滚动的范围
* @param count adapter 中的数量
* @return 在[0总数]范围内滚动位置良好
*/
private static float makeScrollPositionInRange0ToCount(final float currentScrollPosition, final int count) {
float absCurrentScrollPosition = currentScrollPosition;
while (0 > absCurrentScrollPosition) {
absCurrentScrollPosition += count;
}
while (Math.round(absCurrentScrollPosition) >= count) {
absCurrentScrollPosition -= count;
}
return absCurrentScrollPosition;
}
/**
* This interface methods will be called for each visible view item after general LayoutManager layout finishes. <br />
* <br />
* Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting.
*/
@SuppressWarnings("InterfaceNeverImplemented")
public abstract static class PostLayoutListener {
/**
* 子布局完成后调用。通常,您可以在这里进行任何平移和缩放工作。
*
* @param child view that was layout
* @param itemPositionToCenterDiff view center line difference to layout center. if > 0 then this item is bellow layout center line, else if not
* @param orientation layoutManager orientation {@link #getLayoutDirection()}
* @param itemPositionInAdapter item position inside adapter for this layout pass
*/
public ItemTransformation transformChild(
@NonNull final View child,
final float itemPositionToCenterDiff,
final int orientation,
final int itemPositionInAdapter
) {
return transformChild(child, itemPositionToCenterDiff, orientation);
}
/**
* Called after child layout finished. Generally you can do any translation and scaling work here.
*
* @param child view that was layout
* @param itemPositionToCenterDiff view center line difference to layout center. if > 0 then this item is bellow layout center line, else if not
* @param orientation layoutManager orientation {@link #getLayoutDirection()}
*/
public ItemTransformation transformChild(
@NonNull final View child,
final float itemPositionToCenterDiff,
final int orientation
) {
throw new IllegalStateException("at least one transformChild should be implemented");
}
}
public interface OnCenterItemSelectionListener {
/**
* Listener that will be called on every change of center item.
* This listener will be triggered on <b>every</b> layout operation if item was changed.
* Do not do any expensive operations in this method since this will effect scroll experience.
*
* @param adapterPosition current layout center item
*/
void onCenterItemChanged(final int adapterPosition);
}
public interface OnDargAutoDiffListener {
void onDxChanged(final float adapterPosition,final int currentPosition);
}
/**
* Helper class that holds currently visible items.
* Generally this class fills this list. <br />
* <br />
* This class holds all scroll and maxVisible items state.
*
* @see #getMaxVisibleItems()
*/
private static class LayoutHelper {
private int mMaxVisibleItems;
private int mScrollOffset;
private LayoutOrder[] mLayoutOrder;
private final List<WeakReference<LayoutOrder>> mReusedItems = new ArrayList<>();
LayoutHelper(final int maxVisibleItems) {
mMaxVisibleItems = maxVisibleItems;
}
/**
* Called before any fill calls. Needed to recycle old items and init new array list. Generally this list is an array an it is reused.
*
* @param layoutCount items count that will be layout
*/
void initLayoutOrder(final int layoutCount) {
if (null == mLayoutOrder || mLayoutOrder.length != layoutCount) {
if (null != mLayoutOrder) {
recycleItems(mLayoutOrder);
}
mLayoutOrder = new LayoutOrder[layoutCount];
fillLayoutOrder();
}
}
/**
* Called during layout generation process of filling this list. Should be called only after {@link #initLayoutOrder(int)} method call.
*
* @param arrayPosition position in layout order
* @param itemAdapterPosition adapter position of item for future data filling logic
* @param itemPositionDiff difference of current item scroll position and center item position.
* if this is a center item and it is in real center of layout, then this will be 0.
* if current layout is not in the center, then this value will never be int.
* if this item center is bellow layout center line then this value is greater then 0,
* else less then 0.
*/
void setLayoutOrder(final int arrayPosition, final int itemAdapterPosition, final float itemPositionDiff) {
final LayoutOrder item = mLayoutOrder[arrayPosition];
item.mItemAdapterPosition = itemAdapterPosition;
item.mItemPositionDiff = itemPositionDiff;
}
/**
* Checks is this screen Layout has this adapterPosition view in layout
*
* @param adapterPosition adapter position of item for future data filling logic
* @return true is adapterItem is in layout
*/
boolean hasAdapterPosition(final int adapterPosition) {
if (null != mLayoutOrder) {
for (final LayoutOrder layoutOrder : mLayoutOrder) {
if (layoutOrder.mItemAdapterPosition == adapterPosition) {
return true;
}
}
}
return false;
}
@SuppressWarnings("VariableArgumentMethod")
private void recycleItems(@NonNull final LayoutOrder... layoutOrders) {
for (final LayoutOrder layoutOrder : layoutOrders) {
//noinspection ObjectAllocationInLoop
mReusedItems.add(new WeakReference<>(layoutOrder));
}
}
private void fillLayoutOrder() {
for (int i = 0, length = mLayoutOrder.length; i < length; ++i) {
if (null == mLayoutOrder[i]) {
mLayoutOrder[i] = createLayoutOrder();
}
}
}
private LayoutOrder createLayoutOrder() {
final Iterator<WeakReference<LayoutOrder>> iterator = mReusedItems.iterator();
while (iterator.hasNext()) {
final WeakReference<LayoutOrder> layoutOrderWeakReference = iterator.next();
final LayoutOrder layoutOrder = layoutOrderWeakReference.get();
iterator.remove();
if (null != layoutOrder) {
return layoutOrder;
}
}
return new LayoutOrder();
}
}
/**
* Class that holds item data.
* This class is filled during {@link #generateLayoutOrder(float, RecyclerView.State)} and used during {@link #fillData(RecyclerView.Recycler, RecyclerView.State)}
*/
private static class LayoutOrder {
/**
* Item adapter position
*/
private int mItemAdapterPosition;
/**
* Item center difference to layout center. If center of item is bellow layout center, then this value is greater then 0, else it is less.
*/
private float mItemPositionDiff;
}
protected static class CarouselSavedState implements Parcelable {
private final Parcelable mSuperState;
private int mCenterItemPosition;
protected CarouselSavedState(@Nullable final Parcelable superState) {
mSuperState = superState;
}
private CarouselSavedState(@NonNull final Parcel in) {
mSuperState = in.readParcelable(Parcelable.class.getClassLoader());
mCenterItemPosition = in.readInt();
}
protected CarouselSavedState(@NonNull final CarouselSavedState other) {
mSuperState = other.mSuperState;
mCenterItemPosition = other.mCenterItemPosition;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel parcel, final int i) {
parcel.writeParcelable(mSuperState, i);
parcel.writeInt(mCenterItemPosition);
}
public static final Creator<CarouselSavedState> CREATOR
= new Creator<CarouselSavedState>() {
@Override
public CarouselSavedState createFromParcel(final Parcel parcel) {
return new CarouselSavedState(parcel);
}
@Override
public CarouselSavedState[] newArray(final int i) {
return new CarouselSavedState[i];
}
};
}
}

View File

@@ -0,0 +1,40 @@
package com.mogo.och.bus.passenger.ui.layoutmanage;
import android.view.View;
import androidx.annotation.NonNull;
/**
* Implementation of {@link CarouselLayoutManager.PostLayoutListener} that makes interesting scaling of items. <br />
* We are trying to make items scaling quicker for closer items for center and slower for when they are far away.<br />
* Tis implementation uses atan function for this purpose.
*/
public class CarouselZoomPostLayoutListener extends CarouselLayoutManager.PostLayoutListener {
private final float mScaleMultiplier;
public CarouselZoomPostLayoutListener() {
this(0.21f);
}
public CarouselZoomPostLayoutListener(final float scaleMultiplier) {
mScaleMultiplier = scaleMultiplier;
}
@Override
public ItemTransformation transformChild(@NonNull final View child, final float itemPositionToCenterDiff, final int orientation) {
float scale = 1.0f - mScaleMultiplier * Math.abs(itemPositionToCenterDiff);
final float translateY;
final float translateX;
if (CarouselLayoutManager.VERTICAL == orientation) {
final float translateYGeneral = child.getMeasuredHeight() * (1 - scale) / 2f;
translateY = Math.signum(itemPositionToCenterDiff) * translateYGeneral;
translateX = 0;
} else {
final float translateXGeneral = child.getMeasuredWidth() * (1 - scale)/8;
translateX = Math.signum(itemPositionToCenterDiff) * translateXGeneral;
translateY = 0;
}
return new ItemTransformation(scale,scale, scale, 0, translateY);
}
}

View File

@@ -0,0 +1,52 @@
package com.mogo.och.bus.passenger.ui.layoutmanage;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* Class for centering items after scroll event.<br />
* This class will listen to current scroll state and if item is not centered after scroll it will automatically scroll it to center.
*/
public class CenterScrollListener extends RecyclerView.OnScrollListener {
private boolean mAutoSet = true;
@Override
public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {
super.onScrollStateChanged(recyclerView, newState);
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (!(layoutManager instanceof CarouselLayoutManager)) {
mAutoSet = true;
return;
}
final CarouselLayoutManager lm = (CarouselLayoutManager) layoutManager;
if (!mAutoSet) {
if (RecyclerView.SCROLL_STATE_IDLE == newState) {
final int scrollNeeded = lm.getOffsetCenterView();
// 滚动到中心位置
if (CarouselLayoutManager.HORIZONTAL == lm.getOrientation()) {
recyclerView.smoothScrollBy(scrollNeeded, 0);
} else {
recyclerView.smoothScrollBy(0, scrollNeeded);
}
pageSelect(recyclerView,newState);
mAutoSet = true;
}
}
if (RecyclerView.SCROLL_STATE_DRAGGING == newState || RecyclerView.SCROLL_STATE_SETTLING == newState) {
mAutoSet = false;
}
if(RecyclerView.SCROLL_STATE_DRAGGING == newState){
pageStop();
}
}
protected void pageStop() {
}
protected void pageSelect(RecyclerView recyclerView, final int newState) {
}
}

View File

@@ -0,0 +1,18 @@
package com.mogo.och.bus.passenger.ui.layoutmanage;
public class ItemTransformation {
final float mAlpha;
final float mScaleX;
final float mScaleY;
final float mTranslationX;
final float mTranslationY;
public ItemTransformation(final float alpha,final float scaleX, final float scaleY, final float translationX, final float translationY) {
mScaleX = scaleX;
mScaleY = scaleY;
mTranslationX = translationX;
mTranslationY = translationY;
mAlpha = alpha;
}
}

View File

@@ -0,0 +1,96 @@
package com.mogo.och.bus.passenger.utils
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.view.View
import android.view.WindowManager
import com.mogo.och.bus.passenger.view.ConsultVideoPlayer
import com.shuyu.gsyvideoplayer.GSYVideoManager
import java.lang.Exception
/**
* 视频全屏播放
*
* @author yangyakun
*/
@SuppressLint("StaticFieldLeak")
object FullVideoUtils {
private const val TAG = "OverlayViewUtils"
private var windowManager: WindowManager? = null
@Volatile
private var isShowing = false
/**
* 记录上一次的View
*/
private var lastOverlayView: View? = null
/**
* 添加覆盖View在Activity上面
*/
@JvmOverloads
fun showOverlayView(context: Activity, overlayView: View, ani: Int = -1) {
if (windowManager == null) {
windowManager = context.windowManager
}
// 设置View显示模式沉浸式的侵入到状态栏导航栏
overlayView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
val params = WindowManager.LayoutParams()
params.width = WindowManager.LayoutParams.MATCH_PARENT
params.height = WindowManager.LayoutParams.MATCH_PARENT
params.alpha = 1.0f
// 设置窗口类型为应用子窗口和PopupWindow同类型
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
// 没有边界限制,允许窗口扩展到屏幕外
params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
if (ani != -1) {
params.windowAnimations = ani
}
try {
// 后门逻辑,长时间触摸消失
lastOverlayView = overlayView
windowManager!!.addView(overlayView, params)
isShowing = true
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 移除覆盖View在Activity上面
*/
fun dismissOverlayView(needReleas:Boolean) {
if (!isShowing) {
return
}
val consultVideoPlayer =
lastOverlayView?.findViewById<ConsultVideoPlayer>(GSYVideoManager.FULLSCREEN_ID)
consultVideoPlayer?.let {
if(needReleas){
it.onVideoReset()
it.setVideoAllCallBack(null)
it.smalllPlayer?.clearFullscreenLayout(it)
}
consultVideoPlayer.removeAllViews()
}
try {
if (windowManager != null) {
windowManager!!.removeViewImmediate(lastOverlayView)
windowManager = null
}
if (lastOverlayView != null) {
lastOverlayView = null
}
isShowing = false
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -0,0 +1,21 @@
package com.mogo.och.bus.passenger.utils
import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
object ZoomDrawable {
fun zoomDrawableImage(context: Context,@DrawableRes id:Int,scaleX:Float,scaleY:Float):Drawable{
val bitmap: Bitmap = BitmapFactory.decodeResource(context.resources, id)
val bitmapWidth = bitmap.width
val bitmapHeight = bitmap.height
val matrix = Matrix()
matrix.postScale(scaleX, scaleY)
// 产生缩放后的Bitmap对象
val resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, true)
return BitmapDrawable(context.resources,resizeBitmap)
}
}

View File

@@ -0,0 +1,484 @@
package com.mogo.och.bus.passenger.view
import android.app.Activity
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.Surface
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import com.mogo.eagle.core.utilcode.util.TimeTransformUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
import com.mogo.och.bus.passenger.R
import com.mogo.och.bus.passenger.utils.FullVideoUtils
import com.mogo.och.bus.passenger.utils.ZoomDrawable
import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer
import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
import me.jessyan.autosize.utils.AutoSizeUtils
import java.lang.reflect.Constructor
/**
* @author lixiaopeng
* @since 2021/11/3
*
* 视频播放器ui定制
*/
class ConsultVideoPlayer : StandardGSYVideoPlayer {
private lateinit var start: ImageView
lateinit var coverImage: ImageView
private lateinit var currentTimeTextView: TextView
private lateinit var totalTimeTextView: TextView
private lateinit var aivStartPlay: AppCompatImageView
private lateinit var layoutBottom: ConstraintLayout
private lateinit var vPpenLeft: View
private var fullVideoPlayer:ConsultVideoPlayer?=null
var smalllPlayer:ConsultVideoPlayer?=null
private var currentTime = 0
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, fullFlag: Boolean?) : super(context, fullFlag)
override fun init(context: Context) {
mEnlargeImageRes = R.drawable.taxi_p_change_full
super.init(context)
start = findViewById(R.id.start)
coverImage = findViewById(R.id.thumbImage)
currentTimeTextView = findViewById(R.id.current)
totalTimeTextView = findViewById(R.id.total)
aivStartPlay = findViewById(R.id.aiv_start_play)
layoutBottom = findViewById(R.id.layout_bottom)
vPpenLeft = findViewById(R.id.v_open_left)
fullscreenButton.setOnClickListener(this)
aivStartPlay.setOnClickListener(this)
if (mThumbImageViewLayout != null
&& (mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR)
) {
mThumbImageViewLayout.visibility = View.VISIBLE
}
GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL)
aivStartPlay.scaleX = 0.8f
aivStartPlay.scaleY = 0.8f
mProgressBar.thumb = ZoomDrawable.zoomDrawableImage(context,R.drawable.bg_taxi_p_video_index,0.66f,0.66f)
}
private fun addDrageAnchor(){
vPpenLeft.visibility = VISIBLE
layoutBottom.post {
val layoutParams = layoutBottom.layoutParams as ConstraintLayout.LayoutParams
layoutParams.height = AutoSizeUtils.dp2px(context,176f)
layoutBottom.layoutParams = layoutParams
}
mTopContainer.post {
// val layoutParams = mTopContainer.layoutParams as ConstraintLayout.LayoutParams
// layoutParams.height = 320
// mTopContainer.layoutParams = layoutParams
val background = layoutBottom.background as GradientDrawable
val x = arrayOf(12f, 12f,12f, 12f,12f, 12f,12f, 12f)
background.cornerRadii = x.toFloatArray()
layoutBottom.background = background
fullscreenButton.setPadding(92,0,92,0)
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, AutoSizeUtils.dp2px(context,40f).toFloat())
val layoutParams1 = titleTextView.layoutParams as ConstraintLayout.LayoutParams
layoutParams1.marginStart = 80
titleTextView.layoutParams = layoutParams1
aivStartPlay.scaleX = 1f
aivStartPlay.scaleY = 1f
val drawable = ActivityCompat.getDrawable(context, R.drawable.bg_taxi_p_video_index)
mProgressBar.thumb = drawable
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mProgressBar.maxHeight = 6
mProgressBar.minHeight = 6
}
}
}
override fun getLayoutId(): Int {
return R.layout.taxi_p_video_show
}
override fun updateStartImage() {
when (mCurrentState) {
GSYVideoView.CURRENT_STATE_PLAYING ->{
start.setImageResource(R.drawable.notice_video_pause)
aivStartPlay.visibility = View.GONE
}
else -> {
start.setImageResource(R.drawable.notice_video_after_pause)
aivStartPlay.visibility = View.VISIBLE
}
}
}
override fun setStateAndUi(state: Int) {
super.setStateAndUi(state)
if(state==CURRENT_STATE_PLAYING_BUFFERING_START){
ToastUtils.showShort("加载中请稍等")
}
}
override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
super.onWindowFocusChanged(hasWindowFocus)
if(isIfCurrentIsFullscreen&&smalllPlayer!=null){
if(hasWindowFocus){//获取焦点
onVideoResume()
}else{
onVideoPause()
}
}
}
override fun touchDoubleUp() {
}
override fun changeUiToNormal() {
super.changeUiToNormal()
setViewShowState(fullscreenButton, INVISIBLE)
}
override fun changeUiToPlayingShow() {
super.changeUiToPlayingShow()
setViewShowState(fullscreenButton, VISIBLE)
}
public override fun hideAllWidget() {
super.hideAllWidget()
}
override fun setProgressAndTime(
progress: Int,
secProgress: Int,
currentTime: Int,
totalTime: Int,
forceChange: Boolean
) {
super.setProgressAndTime(progress, secProgress, currentTime, totalTime, forceChange)
//时间显示
currentTimeTextView.text = TimeTransformUtils.stringForTime(currentTime)
totalTimeTextView.text = TimeTransformUtils.stringForTime(totalTime)
if(currentTime>=totalTime-3000){//
this.currentTime = -1
}else{
this.currentTime = currentTime
}
if (progress != 0) {
mProgressBar?.progress = progress
}
}
override fun showWifiDialog() {
//直接播放不显示WIFI对话框
startPlayLogic()
}
override fun onDetachedFromWindow() {
mProgressBar?.progress = 0
fullVideoPlayer?.let {
clearFullscreenLayout(it)
}
fullVideoPlayer = null
if(!isIfCurrentIsFullscreen) {
onVideoReset()
setVideoAllCallBack(null)
}
dismissProgressDialog()
super.onDetachedFromWindow()
}
override fun onClick(v: View?) {
super.onClick(v)
when (v?.id) {
R.id.fullscreen -> {
startWindowFullscreenOwn(context)
// startWindowFullscreen(context)
}
R.id.aiv_start_play -> {
if(currentState==GSYVideoView.CURRENT_STATE_PAUSE){
onVideoResume(false)
}else{
if (mProgressBar==null) {
startPlayLogic()
}else {
mProgressBar?.let {
if(currentTime>0) {
seekOnStart = currentTime.toLong()
}
startPlayLogic()
}
}
}
}
else -> {}
}
}
override fun onCompletion() {
start.setImageResource(R.drawable.notice_video_after_pause)
}
override fun onSurfaceUpdated(surface: Surface) {
super.onSurfaceUpdated(surface)
if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) {
mThumbImageViewLayout.visibility = View.INVISIBLE
}
}
override fun onPrepared() {
super.onPrepared()
}
override fun onBufferingUpdate(percent: Int) {
super.onBufferingUpdate(percent)
}
override fun onError(what: Int, extra: Int) {
super.onError(what, extra)
mThumbImageViewLayout?.visibility = View.VISIBLE
ToastUtils.showLong("哎呀,出错了,看看其他视频吧")
currentTime = -1
if(isIfCurrentIsFullscreen){
smalllPlayer?.clearFullscreenLayout(this)
smalllPlayer?.currentTime = -1
FullVideoUtils.dismissOverlayView(false)
}
}
override fun setViewShowState(view: View?, visibility: Int) {
if (view === mThumbImageViewLayout && visibility != View.VISIBLE) {
return
}
super.setViewShowState(view, visibility)
}
override fun onSurfaceAvailable(surface: Surface) {
super.onSurfaceAvailable(surface)
if (GSYVideoType.getRenderType() != GSYVideoType.TEXTURE) {
if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) {
mThumbImageViewLayout.visibility = View.INVISIBLE
}
}
}
override fun onAutoCompletion() {
super.onAutoCompletion()
if(mIfCurrentIsFullscreen){
if(smalllPlayer!=null){
smalllPlayer?.clearFullscreenLayout(this)
}
FullVideoUtils.dismissOverlayView(false)
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (!mIfCurrentIsFullscreen) {
this.outlineProvider = TextureVideoViewOutlineProvider(38F)
this.clipToOutline = true
}
}
private fun startWindowFullscreenOwn(context:Context){
val gsyBaseVideoPlayer = startWindowFullscreen(context)
gsyBaseVideoPlayer?.let {
val gsyVideoPlayer = it as StandardGSYVideoPlayer
gsyVideoPlayer.setLockClickListener(mLockClickListener)
gsyVideoPlayer.isNeedLockFull = isNeedLockFull
initFullUI(gsyVideoPlayer)
}
}
private fun initFullUI(standardGSYVideoPlayer: StandardGSYVideoPlayer) {
if (mBottomProgressDrawable != null) {
standardGSYVideoPlayer.setBottomProgressBarDrawable(mBottomProgressDrawable)
}
if (mBottomShowProgressDrawable != null && mBottomShowProgressThumbDrawable != null) {
standardGSYVideoPlayer.setBottomShowProgressBarDrawable(
mBottomShowProgressDrawable,
mBottomShowProgressThumbDrawable
)
}
if (mVolumeProgressDrawable != null) {
standardGSYVideoPlayer.setDialogVolumeProgressBar(mVolumeProgressDrawable)
}
if (mDialogProgressBarDrawable != null) {
standardGSYVideoPlayer.setDialogProgressBar(mDialogProgressBarDrawable)
}
if (mDialogProgressHighLightColor >= 0 && mDialogProgressNormalColor >= 0) {
standardGSYVideoPlayer.setDialogProgressColor(
mDialogProgressHighLightColor,
mDialogProgressNormalColor
)
}
standardGSYVideoPlayer.titleTextView?.text = titleTextView.text
}
private fun startWindowFullscreen(context:Context):GSYBaseVideoPlayer?{
if (mTextureViewContainer.childCount > 0) {
mTextureViewContainer.removeAllViews()
}
var hadNewConstructor = true
//切换时关闭非全屏定时器
cancelProgressTimer()
try {
this@ConsultVideoPlayer.javaClass.getConstructor(
Context::class.java,
Boolean::class.java
)
} catch (e: java.lang.Exception) {
hadNewConstructor = false
}
try {
//通过被重载的不同构造器来选择
val constructor: Constructor<ConsultVideoPlayer>
val gsyVideoPlayer: ConsultVideoPlayer
if (!hadNewConstructor) {
constructor = this@ConsultVideoPlayer.javaClass.getConstructor(Context::class.java)
gsyVideoPlayer = constructor.newInstance(mContext)
} else {
constructor = this@ConsultVideoPlayer.javaClass.getConstructor(
Context::class.java,
Boolean::class.java
)
gsyVideoPlayer = constructor.newInstance(mContext, true)
}
this.fullVideoPlayer = gsyVideoPlayer
gsyVideoPlayer.id = fullId
gsyVideoPlayer.isIfCurrentIsFullscreen = true
gsyVideoPlayer.setVideoAllCallBack(mVideoAllCallBack)
gsyVideoPlayer.addDrageAnchor()
cloneParams(this, gsyVideoPlayer)
val frameLayout = FrameLayout(context)
if (gsyVideoPlayer.fullscreenButton != null) {
gsyVideoPlayer.fullscreenButton.setImageResource(R.drawable.taxi_p_change_normal)
gsyVideoPlayer.start.setImageResource(R.drawable.notice_video_pause_big)
gsyVideoPlayer.fullscreenButton.setOnClickListener { v ->
if (mBackFromFullScreenListener == null) {
clearFullscreenLayout(gsyVideoPlayer)
FullVideoUtils.dismissOverlayView(false)
} else {
mBackFromFullScreenListener.onClick(v)
}
}
}
gsyVideoPlayer.smalllPlayer = this
frameLayout.setBackgroundColor(Color.BLACK)
val lp = LayoutParams(width, height)
frameLayout.addView(gsyVideoPlayer, lp)
FullVideoUtils.showOverlayView(context as Activity,frameLayout,R.style.och_window_anim_alpha)
gsyVideoPlayer.visibility = INVISIBLE
frameLayout.visibility = INVISIBLE
resolveFullVideoShow(context, gsyVideoPlayer, frameLayout)
gsyVideoPlayer.addTextureView()
gsyVideoPlayer.startProgressTimer()
gsyVideoManager.setLastListener(this)
gsyVideoManager.setListener(gsyVideoPlayer)
checkoutState()
thumbImageViewLayout.visibility = View.VISIBLE
return gsyVideoPlayer
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return null
}
/**
* 全屏
*/
override fun resolveFullVideoShow(context: Context?, gsyVideoPlayer: GSYBaseVideoPlayer,
frameLayout: FrameLayout) {
val lp = gsyVideoPlayer.layoutParams as LayoutParams
lp.setMargins(0, 0, 0, 0)
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
lp.gravity = Gravity.BOTTOM
gsyVideoPlayer.layoutParams = lp
gsyVideoPlayer.isIfCurrentIsFullscreen = true
val isVertical = isVerticalFullByVideoSize
val isLockLand = isLockLandByAutoFullSize
if (isShowFullAnimation) {
mInnerHandler.postDelayed({ //autoFull模式下非横屏视频视频不横屏并且不自动旋转
if (!isVertical && isLockLand && mOrientationUtils != null && mOrientationUtils.isLand != 1) {
mOrientationUtils.resolveByClick()
}
gsyVideoPlayer.visibility = VISIBLE
frameLayout.visibility = VISIBLE
}, 300)
} else {
if (!isVertical && isLockLand && mOrientationUtils != null) {
mOrientationUtils.resolveByClick()
}
gsyVideoPlayer.visibility = VISIBLE
frameLayout.visibility = VISIBLE
}
if (mVideoAllCallBack != null) {
mVideoAllCallBack.onEnterFullscreen(mOriginUrl, mTitle, gsyVideoPlayer)
}
mIfCurrentIsFullscreen = true
checkoutState()
checkAutoFullWithSizeAndAdaptation(gsyVideoPlayer)
}
fun getStatusBarHeight(): Int{
return Math.ceil((25 * context.resources.displayMetrics.density).toDouble()).toInt()
}
fun clearFullscreenLayout(gsyVideoPlayer:ConsultVideoPlayer) {
mIfCurrentIsFullscreen = false
val delay = 100
gsyVideoPlayer.smalllPlayer = null
mInnerHandler.postDelayed({ resolveNormalVideoShow(gsyVideoPlayer) }, delay.toLong())
}
private fun resolveNormalVideoShow(gsyVideoPlayer: GSYVideoPlayer) {
mCurrentState = gsyVideoManager.lastState
cloneParams(gsyVideoPlayer, this)
gsyVideoManager.setListener(gsyVideoManager.lastListener())
gsyVideoManager.setLastListener(null)
gsyVideoPlayer.setVideoAllCallBack(null)
setStateAndUi(mCurrentState)
addTextureView()
mSaveChangeViewTIme = System.currentTimeMillis()
if (mVideoAllCallBack != null) {
mVideoAllCallBack.onQuitFullscreen(mOriginUrl, mTitle, this)
}
mIfCurrentIsFullscreen = false
if (fullscreenButton != null) {
fullscreenButton.setImageResource(enlargeImageRes)
}
this.fullVideoPlayer = null
}
fun getVideoAllCallBack(): VideoAllCallBack? {
return mVideoAllCallBack
}
}

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000" android:fromAlpha="0" android:toAlpha="1" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dp"/>
<solid android:color="#99D8D8D8" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="5dp"/>
<solid android:color="#66FFFFFF" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<gradient android:startColor="#303CFF" android:centerColor="#216CFF" android:endColor="#25C1F9" android:angle="0"/>
</shape>
</scale>
</item>
</layer-list>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient android:startColor="#80000000" android:endColor="#00000000" android:angle="90"/>
<corners android:radius="8dp"/>
</shape>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="38dp"
android:bottomRightRadius="36dp"
android:topLeftRadius="36dp"
android:topRightRadius="36dp" />
<solid android:color="#80051025" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient android:endColor="#80000000" android:startColor="#00000000" android:angle="90"/>
</shape>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dp"/>
<solid android:color="#99D8D8D8" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="5dp"/>
<solid android:color="#66FFFFFF" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<gradient android:startColor="#303CFF" android:centerColor="#216CFF" android:endColor="#25C1F9"/>
</shape>
</scale>
</item>
</layer-list>

View File

@@ -2,16 +2,16 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clickable="true"
android:background="@android:color/holo_orange_dark"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView <androidx.recyclerview.widget.RecyclerView
android:text="video" android:id="@+id/rvVideoPlaylist"
android:layout_width="match_parent"
android:layout_height="731dp"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="wrap_content" />
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/bg_taxi_p_video_bg_shape"
android:layout_width="@dimen/dp_1300"
android:layout_height="@dimen/dp_731">
<com.mogo.och.bus.passenger.view.ConsultVideoPlayer
android:id="@+id/video_item_player"
android:layout_gravity="center"
android:layout_width="@dimen/dp_1300"
android:layout_height="@dimen/dp_731" />
</FrameLayout>

View File

@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_video_cover"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/surface_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"/>
<RelativeLayout
android:id="@+id/thumb"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/thumbImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:scaleType="fitXY" />
</RelativeLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/aiv_start_play"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/taxi_p_mogo_video_play"
android:layout_width="@dimen/dp_180"
android:layout_height="@dimen/dp_180"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_top"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="@drawable/bg_taxi_p_video_bg_top"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="40dp"
android:textColor="@android:color/white"
android:textSize="28dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--局部播放器-->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_bottom"
android:layout_width="0dp"
android:background="@drawable/bg_taxi_p_video_bg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_height="@dimen/dp_119"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:id="@+id/start"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/total"
app:layout_constraintBottom_toBottomOf="@+id/total"
android:paddingStart="@dimen/dp_42"
android:layout_width="@dimen/dp_98"
android:paddingEnd="@dimen/dp_28"
android:layout_height="match_parent"
android:src="@drawable/notice_video_pause" />
<TextView
android:id="@+id/current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@+id/start"
app:layout_constraintTop_toTopOf="@+id/total"
app:layout_constraintBottom_toBottomOf="@+id/total"
android:gravity="bottom"
android:text="02:23"
android:textColor="@android:color/white"
android:textSize="26dp" />
<SeekBar
android:id="@+id/progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_31"
app:layout_constraintStart_toEndOf="@+id/current"
app:layout_constraintEnd_toStartOf="@+id/total"
app:layout_constraintTop_toTopOf="@+id/total"
app:layout_constraintBottom_toBottomOf="@+id/total"
android:max="100"
android:maxHeight="@dimen/dp_7"
android:minHeight="@dimen/dp_7"
android:splitTrack="false"
android:progressDrawable="@drawable/taxi_video_seekbar_style"
android:thumb="@drawable/bg_taxi_p_video_index" />
<TextView
android:id="@+id/total"
android:layout_width="@dimen/dp_106"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@+id/fullscreen"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="bottom"
android:textSize="26dp"
android:textColor="@android:color/white"/>
<ImageView
android:id="@+id/fullscreen"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="@dimen/dp_140"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:src="@drawable/notice_video_pause_small" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/v_open_left"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:visibility="gone"
android:layout_width="143dp"
android:layout_height="308dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -20,6 +20,10 @@
<item name="android:backgroundDimEnabled">false</item> <item name="android:backgroundDimEnabled">false</item>
</style> </style>
<style name="och_window_anim_alpha">
<item name="android:windowEnterAnimation">@anim/alpha_hide_show</item>
</style>
</resources> </resources>

View File

@@ -56,6 +56,7 @@ dependencies {
implementation project(":OCH:mogo-och-common-module") implementation project(":OCH:mogo-och-common-module")
compileOnly project(":libraries:mogo-map") compileOnly project(":libraries:mogo-map")
implementation project(':core:mogo-core-res')
} }