[Update]调整全览模式摄像头直播显示

This commit is contained in:
chenfufeng
2022-07-25 20:51:37 +08:00
parent 7c75757ab0
commit 0c861fd499
41 changed files with 1493 additions and 109 deletions

View File

@@ -32,6 +32,7 @@ import com.mogo.eagle.core.data.constants.MoGoFragmentPaths
import com.mogo.eagle.core.data.enums.SidePattern
import com.mogo.eagle.core.data.enums.SidePattern.*
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
import com.mogo.eagle.core.data.map.Infrastructure
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData
import com.mogo.eagle.core.data.report.ReportEntity
@@ -170,10 +171,10 @@ import java.util.*
}
}
ivCameraIcon?.setOnLongClickListener {
showSmallFragment()
/*ivCameraIcon?.setOnLongClickListener {
activity?.let { it1 -> CarcorderPreviewView.show(it1) }
true
}
}*/
ivToolsIcon?.setOnClickListener {
if (toolsViewFloat == null) {
@@ -310,12 +311,12 @@ import java.util.*
mViewNotificationProvider = provider
}
override fun showVideoDialog(url: String, isFlvUrl: Boolean) {
override fun showVideoDialog(infList: List<Infrastructure>) {
context?.let {
if (roadVideoDialog == null) {
roadVideoDialog = RoadVideoDialog(it)
}
roadVideoDialog?.show(url, isFlvUrl)
roadVideoDialog?.show(infList)
}
}

View File

@@ -1,16 +1,21 @@
package com.mogo.eagle.core.function.hmi.ui.camera
import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.view.View
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.data.map.Infrastructure
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.widget.indicator.enums.IndicatorOrientation
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode
import com.mogo.eagle.core.widget.indicator.enums.IndicatorStyle
import com.mogo.eagle.core.widget.media.video.SimpleVideoPlayer
import com.mogo.module.common.dialog.BaseFloatDialog
import com.shuyu.gsyvideoplayer.GSYVideoManager
@@ -38,6 +43,12 @@ class RoadVideoDialog(context: Context) : BaseFloatDialog(context), LifecycleObs
GSYVideoOptionBuilder()
}
private val infList by lazy {
ArrayList<Infrastructure>()
}
private var curPlayIndex = 0
init {
setContentView(R.layout.road_video_dialog)
roadVideoPB.indeterminateDrawable.colorFilter = PorterDuffColorFilter(
@@ -45,7 +56,25 @@ class RoadVideoDialog(context: Context) : BaseFloatDialog(context), LifecycleObs
PorterDuff.Mode.MULTIPLY
)
setCanceledOnTouchOutside(false)
roundLayout.setScrollListener { is2Left ->
/**
* 如果画面向左滚动
*/
val calIndex = if (is2Left) {
curPlayIndex - 1
} else {
curPlayIndex + 1
}
// 检查数组越界
if (calIndex >= 0 && calIndex < infList.size) {
curPlayIndex = calIndex
infList[curPlayIndex].ip?.let {
playVideo(it)
}
indicatorView.onPageScrolled(calIndex, 0f, 0)
indicatorView.onPageSelected(calIndex)
}
}
roadVideoClose.setOnClickListener {
dismiss()
}
@@ -53,19 +82,24 @@ class RoadVideoDialog(context: Context) : BaseFloatDialog(context), LifecycleObs
initVideoPlayer()
}
fun show(url: String, isFlvUrl: Boolean) {
fun show(data: List<Infrastructure>) {
show()
infList.clear()
infList.addAll(data)
initIndicatorView()
// 打开指定ip的摄像头直播流
infList[0].ip?.let {
playVideo(it)
}
}
private fun playVideo(ip: String) {
roadVideoPB.visibility = View.VISIBLE
if (!isFlvUrl) {
// 打开指定ip的摄像头直播流
CallerMonitorManager.openCameraStream(url, { flvUrl ->
gsyVideoPlay(flvUrl)
}) {
ToastUtils.showShort(it.message)
dismiss()
}
} else {
gsyVideoPlay(url)
CallerMonitorManager.openCameraStream(ip, { flvUrl ->
gsyVideoPlay(flvUrl)
}) {
roadVideoPB.visibility = View.GONE
ToastUtils.showShort(it.message)
}
}
@@ -74,6 +108,20 @@ class RoadVideoDialog(context: Context) : BaseFloatDialog(context), LifecycleObs
stopLive()
}
private fun initIndicatorView() {
indicatorView.notifyDataChanged(infList.size)
indicatorView.setSlideMode(IndicatorSlideMode.SCALE)
indicatorView.setOrientation(IndicatorOrientation.INDICATOR_HORIZONTAL)
indicatorView.setIndicatorStyle(IndicatorStyle.ROUND_RECT)
indicatorView.setSliderColor(
Color.parseColor("#FFFFFF"), Color.parseColor("#26C5FD"),
Color.parseColor("#26C5FD")
)
indicatorView.setSliderWidth(9f, 54f)
indicatorView.setSliderHeight(9f)
indicatorView.setSliderGap(36f)
}
private fun initVideoPlayer() {
val list: MutableList<VideoOptionModel> = ArrayList()
list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 3))

View File

@@ -1,22 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<com.mogo.eagle.core.widget.RoundConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="1736px"
android:minHeight="974px"
app:roundLayoutRadius="30px"
android:background="@drawable/road_video_bg"
tools:ignore="MissingDefaultResource">
android:minHeight="1043px"
tools:ignore="MissingDefaultResource"
>
<com.mogo.eagle.core.widget.media.video.SimpleVideoPlayer
android:id="@+id/roadVideoPlayer"
android:layout_width="1736px"
android:layout_height="974px"
android:visibility="visible"
<com.mogo.eagle.core.widget.RoundConstraintLayout
android:id="@+id/roundLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:roundLayoutRadius="30px"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
android:background="@drawable/road_video_bg"
android:minWidth="1736px"
android:minHeight="974px"
>
<com.mogo.eagle.core.widget.media.video.SimpleVideoPlayer
android:id="@+id/roadVideoPlayer"
android:layout_width="1736px"
android:layout_height="974px"
android:visibility="visible"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</com.mogo.eagle.core.widget.RoundConstraintLayout>
<com.mogo.eagle.core.widget.indicator.IndicatorView
android:id="@+id/indicatorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:vpi_orientation="horizontal"
app:vpi_slide_mode="scale"
app:vpi_slider_checked_color="@color/hmi_traffic_light_red_color_up"
app:vpi_slider_normal_color="@color/hmi_check_keyboard_input_field"
app:vpi_slider_radius="@dimen/dp_20"
app:vpi_style="round_rect"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/roundLayout"
android:layout_marginTop="60px"
/>
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/roadVideoPB"
@@ -41,4 +68,4 @@
app:layout_constraintTop_toTopOf="parent"
/>
</com.mogo.eagle.core.widget.RoundConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -7,4 +7,5 @@
<color name="notice_traffic_line">#555C7E</color>
<color name="notice_dialog_back">#3B4577</color>
<color name="acc_default_txt_color">#FF343C63</color>
<color name="hmi_check_keyboard_input_field">#FF282F62</color>
</resources>

View File

@@ -65,4 +65,5 @@
<color name="p_default_txt_color">#FF6E8EC9</color>
<color name="dark_mode_select_txt_color">#FFFFFFFF</color>
<color name="hmi_check_keyboard_input_field">#FF282F62</color>
</resources>

View File

@@ -12,6 +12,7 @@ import com.alibaba.android.arouter.facade.annotation.Route;
import com.mogo.commons.mvp.MvpFragment;
import com.mogo.eagle.core.data.constants.MoGoFragmentPaths;
import com.mogo.eagle.core.data.map.CenterLine;
import com.mogo.eagle.core.data.map.Infrastructure;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPlanningListener;
import com.mogo.eagle.core.function.api.map.hd.IMoGoMapFragmentProvider;
import com.mogo.eagle.core.function.api.setting.IMoGoSkinModeChangeListener;
@@ -19,7 +20,6 @@ import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPlanningListen
import com.mogo.eagle.core.function.call.map.CallerHDMapManager;
import com.mogo.eagle.core.function.call.setting.CallerSkinModeListenerManager;
import com.mogo.eagle.core.function.overview.InfStructureManager;
import com.mogo.eagle.core.function.overview.Infrastructure;
import com.mogo.eagle.core.function.overview.ViewModelExtKt;
import com.mogo.eagle.core.function.overview.vm.OverViewModel;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
@@ -46,8 +46,7 @@ import mogo.telematics.pad.MessagePad;
*/
@Route(path = MoGoFragmentPaths.PATH_FRAGMENT_MAP)
public class MapFragment extends MvpFragment<MapView, MapPresenter>
implements MapView, IMoGoMapFragmentProvider, IMoGoSkinModeChangeListener,
IMoGoAutopilotPlanningListener {
implements MapView, IMoGoMapFragmentProvider, IMoGoSkinModeChangeListener {
private static final String TAG = "MapFragment";
@@ -340,31 +339,4 @@ public class MapFragment extends MvpFragment<MapView, MapPresenter>
public void setDebugMode(boolean debugMode) {
MapAutoApi.INSTANCE.setDebugMode(debugMode);
}
@Override
public void onAutopilotTrajectory(@NonNull List<MessagePad.TrajectoryPoint> trajectoryInfos) {
}
@Override
public void onAutopilotRotting(@Nullable MessagePad.GlobalPathResp globalPathResp) {
if (globalPathResp != null) {
HashMap<String, ArrayList<Infrastructure>> pathMap = new HashMap();
String geoHash;
ArrayList<Infrastructure> infList;
for (MessagePad.Location location : globalPathResp.getWayPointsList()) {
geoHash = GeoHash.withCharacterPrecision(location.getLatitude(), location.getLongitude(), 7).toBase32();
// 网格内的轨迹点只取一次
if (!pathMap.containsKey(geoHash)) {
// 从缓存的新基建数据中去取对应geoHash的新基建数据集合
infList = InfStructureManager.INSTANCE.getData().get(geoHash);
if (infList != null) {
pathMap.put(geoHash, infList);
}
}
}
// 全局路径规划只回调一次保存供全览模式所在Fragment使用
InfStructureManager.INSTANCE.savePathData(pathMap);
}
}
}

View File

@@ -1,5 +1,7 @@
package com.mogo.eagle.core.function.overview
import com.mogo.eagle.core.data.map.Infrastructure
/**
* 本地数据库查询出来的红绿灯、摄像头等数据
*/

View File

@@ -2,6 +2,7 @@ package com.mogo.eagle.core.function.overview
import androidx.room.Dao
import androidx.room.Query
import com.mogo.eagle.core.data.map.Infrastructure
@Dao
interface OverviewDao {

View File

@@ -4,6 +4,7 @@ import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.mogo.eagle.core.data.map.Infrastructure
@Database(entities = [Infrastructure::class], version = 1, exportSchema = false)
abstract class OverviewDb: RoomDatabase() {

View File

@@ -1,7 +1,7 @@
package com.mogo.eagle.core.function.overview.vm
import androidx.lifecycle.*
import com.mogo.eagle.core.function.overview.Infrastructure
import com.mogo.eagle.core.data.map.Infrastructure
import com.mogo.eagle.core.function.overview.OverviewDao
import kotlinx.coroutines.launch
import java.lang.Exception

View File

@@ -1,7 +1,9 @@
package com.mogo.eagle.core.function.smp;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.location.Location;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -50,6 +52,7 @@ import com.autonavi.tbt.TrafficFacilityInfo;
import com.elegant.utils.UiThreadHandler;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.map.Infrastructure;
import com.mogo.eagle.core.data.map.MogoLatLng;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPlanningListener;
@@ -61,7 +64,6 @@ import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.function.call.map.CallerMapLocationListenerManager;
import com.mogo.eagle.core.function.map.R;
import com.mogo.eagle.core.function.overview.InfStructureManager;
import com.mogo.eagle.core.function.overview.Infrastructure;
import com.mogo.eagle.core.function.smp.view.ISmallMapDirectionView;
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils;
import com.mogo.eagle.core.utilcode.mogo.MapAssetStyleUtils;
@@ -112,7 +114,7 @@ public class AMapCustomView
// 全局路径规划中的GeoHash网格
private Map<String, ArrayList<Infrastructure>> pathMap = new HashMap();
private Map<LatLng, Infrastructure> posInfMap = new HashMap();
private Map<LatLng, ArrayList<Infrastructure>> posInfMap = new HashMap();
public AMapCustomView(Context context) {
this(context, null);
@@ -146,8 +148,7 @@ public class AMapCustomView
CallerMapLocationListenerManager.INSTANCE.addListener(TAG, this);
//设置全览模式
overLayerView.setOnClickListener(v -> {
CallerHmiManager.INSTANCE.hideSmallFragment();
// mAMapNaviView.displayOverview();
mAMapNaviView.displayOverview();
});
// 注册定位监听
CallerMapLocationListenerManager.INSTANCE.addListener(TAG, this);
@@ -744,51 +745,40 @@ public class AMapCustomView
}
ArrayList<MarkerOptions> markerOptionsList = new ArrayList();
for (ArrayList<Infrastructure> structureList : infStruMap.values()) {
int firstQuadrantCt = 0, secondQuadrantCt = 0, thirdQuadrantCt = 0, forthQuadrantCt = 0;
// 每个GeoHash内根据坐标系象限分散开摄像头icon显示
for (Infrastructure structure : structureList) {
MarkerOptions markerOption = new MarkerOptions();
LatLng latLng = new LatLng(Double.valueOf(structure.getLat()),
Double.valueOf(structure.getLon()));
markerOption.position(latLng);
if (structure.getHeading() >= 0 && structure.getHeading() <= 90) {
markerOption.anchor(0.5f * firstQuadrantCt, 0.5f + 0.5f * firstQuadrantCt);
markerOption.icon(BitmapDescriptorFactory.fromBitmap(
BitmapFactory.decodeResource(getResources(),R.drawable.video_icon_right)
));
firstQuadrantCt++;
} else if (structure.getHeading() >= 90 && structure.getHeading() <= 180) {
markerOption.anchor(0.5f * secondQuadrantCt, 0.5f * secondQuadrantCt);
markerOption.icon(BitmapDescriptorFactory.fromBitmap(
BitmapFactory.decodeResource(getResources(),R.drawable.video_icon_right)
));
secondQuadrantCt++;
} else if (structure.getHeading() >= 180 && structure.getHeading() <= 270) {
markerOption.anchor(0.5f + 0.5f * thirdQuadrantCt, 0.5f * thirdQuadrantCt);
markerOption.icon(BitmapDescriptorFactory.fromBitmap(
BitmapFactory.decodeResource(getResources(),R.drawable.video_icon_left)
));
thirdQuadrantCt++;
} else {
markerOption.anchor(0.75f + 0.25f * forthQuadrantCt, 0.75f + 0.25f * forthQuadrantCt);
markerOption.icon(BitmapDescriptorFactory.fromBitmap(
BitmapFactory.decodeResource(getResources(),R.drawable.video_icon_left)
));
forthQuadrantCt++;
}
posInfMap.put(latLng, structure);
markerOptionsList.add(markerOption);
}
MarkerOptions markerOption = new MarkerOptions();
LatLng latLng = new LatLng(Double.valueOf(structureList.get(0).getLat()),
Double.valueOf(structureList.get(0).getLon()));
markerOption.position(latLng);
Bitmap bitmap = getBitmap(structureList.size());
markerOption.icon(BitmapDescriptorFactory.fromBitmap(
bitmap
));
posInfMap.put(latLng, structureList);
markerOptionsList.add(markerOption);
}
mAMap.addMarkers(markerOptionsList, false);
mAMap.setOnMarkerClickListener(marker -> {
Infrastructure infrastructure = posInfMap.get(marker.getPosition());
List<Infrastructure> infList = posInfMap.get(marker.getPosition());
// 如果是摄像头
if (0 == infrastructure.getCategory() && infrastructure.getIp() != null) {
CallerHmiManager.INSTANCE.showVideoDialog(infrastructure.getIp());
if (infList != null) {
CallerHmiManager.INSTANCE.showVideoDialog(infList);
return true;
}
return false;
});
}
private Bitmap getBitmap(int count) {
MakerWithCount marker = new MakerWithCount(getContext());
marker.setCount(count);
marker.measure(View.MeasureSpec.makeMeasureSpec(110, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(108, View.MeasureSpec.EXACTLY));
marker.layout(0, 0, marker.getMeasuredWidth(), marker.getMeasuredHeight());
Bitmap bitmap = Bitmap.createBitmap(marker.getWidth(), marker.getHeight(), Bitmap.Config.ARGB_8888);
marker.draw(new Canvas(bitmap));
return bitmap;
}
}

View File

@@ -0,0 +1,27 @@
package com.mogo.eagle.core.function.smp
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.map.R
import kotlinx.android.synthetic.main.view_maker_with_count.view.*
class MakerWithCount @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout (
context,
attrs,
defStyleAttr
) {
init {
LayoutInflater.from(context).inflate(R.layout.view_maker_with_count, this, true)
}
fun setCount(count: Int) {
tvNum.text = count.toString()
}
}

View File

@@ -18,7 +18,7 @@ import com.mogo.eagle.core.function.api.map.smp.IMogoSmallMapProvider;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPlanningListenerManager;
import com.mogo.eagle.core.function.map.R;
import com.mogo.eagle.core.function.overview.InfStructureManager;
import com.mogo.eagle.core.function.overview.Infrastructure;
import com.mogo.eagle.core.data.map.Infrastructure;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import org.jetbrains.annotations.NotNull;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="108px"
android:maxHeight="108px"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/ivIcon"
android:layout_width="80px"
android:layout_height="86px"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="10px"
android:background="@drawable/video_icon_right"
android:visibility="visible"
/>
<TextView
android:id="@+id/tvNum"
android:layout_width="38px"
android:layout_height="38px"
android:background="@drawable/count_bg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textColor="@color/white"
android:gravity="center"
android:textSize="21px"
tools:text="18"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,4 +1,4 @@
package com.mogo.eagle.core.function.overview
package com.mogo.eagle.core.data.map
import androidx.room.ColumnInfo
import androidx.room.Entity

View File

@@ -3,6 +3,7 @@ package com.mogo.eagle.core.function.api.hmi.warning
import android.view.View
import com.mogo.eagle.core.data.bindingcar.IPCUpgradeStateInfo
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
import com.mogo.eagle.core.data.map.Infrastructure
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData
import com.mogo.eagle.core.data.report.ReportEntity
@@ -249,5 +250,5 @@ interface IMoGoWaringProvider : IMoGoHmiViewProxy {
*/
fun showIPCReportWindow(errorReportList: ArrayList<ReportEntity>,warningReportList: ArrayList<ReportEntity>,reportLevel: Int)
fun showVideoDialog(url: String, isFlvUrl: Boolean)
fun showVideoDialog(infList: List<Infrastructure>)
}

View File

@@ -5,6 +5,7 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.bindingcar.IPCUpgradeStateInfo
import com.mogo.eagle.core.data.constants.MoGoFragmentPaths
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
import com.mogo.eagle.core.data.map.Infrastructure
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData
import com.mogo.eagle.core.data.report.ReportEntity
@@ -360,8 +361,7 @@ object CallerHmiManager : CallerBase() {
waringProviderApi?.showIPCReportWindow(errorReportList,warningReportList,reportLevel)
}
@JvmOverloads
fun showVideoDialog(url: String, isFlvUrl: Boolean = false) {
waringProviderApi?.showVideoDialog(url, isFlvUrl)
fun showVideoDialog(infList: List<Infrastructure>) {
waringProviderApi?.showVideoDialog(infList)
}
}

View File

@@ -6,11 +6,11 @@ import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.constraintlayout.widget.ConstraintLayout;
/**
* author : donghongyu
* e-mail : 1358506549@qq.com
@@ -18,12 +18,18 @@ import androidx.constraintlayout.widget.ConstraintLayout;
* desc :
* version: 1.0
*/
public class RoundConstraintLayout extends ConstraintLayout {
public class RoundConstraintLayout extends ConstraintLayout {
private float roundLayoutRadius = 14f;
private Path roundPath;
private RectF rectF;
// 上次滑动的坐标
private int mLastXIntercept;
private int mLastYIntercept;
private IScrollListener mScrollListener;
public RoundConstraintLayout(Context context) {
this(context, null);
}
@@ -61,6 +67,10 @@ public class RoundConstraintLayout extends ConstraintLayout {
postInvalidate();
}
public void setScrollListener(IScrollListener listener) {
mScrollListener = listener;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
@@ -76,4 +86,44 @@ public class RoundConstraintLayout extends ConstraintLayout {
super.draw(canvas);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
if (mScrollListener != null) {
// 向左滑动,则向右滚动
mScrollListener.onScroll(x - mLastXIntercept >= 0);
}
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
default:
break;
}
mLastXIntercept = x;
mLastYIntercept = y;
return intercepted;
}
public interface IScrollListener {
/**
*
* @param is2Left : 画面向左滚动
*/
void onScroll(boolean is2Left);
}
}

View File

@@ -0,0 +1,68 @@
package com.mogo.eagle.core.widget.indicator
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import com.mogo.eagle.core.widget.indicator.annotation.AIndicatorOrientation
import com.mogo.eagle.core.widget.indicator.base.BaseIndicatorView
import com.mogo.eagle.core.widget.indicator.drawer.DrawerProxy
import com.mogo.eagle.core.widget.indicator.enums.IndicatorOrientation
import com.mogo.eagle.core.widget.indicator.option.AttrsController
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
class IndicatorView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : BaseIndicatorView(context, attrs, defStyleAttr) {
private var mDrawerProxy: DrawerProxy
init {
AttrsController.initAttrs(context, attrs, mIndicatorOptions)
mDrawerProxy = DrawerProxy(mIndicatorOptions)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
mDrawerProxy.onLayout(changed, left, top, right, bottom)
}
override fun onMeasure(
widthMeasureSpec: Int,
heightMeasureSpec: Int
) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val measureResult = mDrawerProxy.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(measureResult.measureWidth, measureResult.measureHeight)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
rotateCanvas(canvas)
mDrawerProxy.onDraw(canvas)
}
override fun setIndicatorOptions(options: IndicatorOptions) {
super.setIndicatorOptions(options)
mDrawerProxy.setIndicatorOptions(options)
}
override fun notifyDataChanged(itemCount:Int) {
mDrawerProxy = DrawerProxy(mIndicatorOptions)
super.notifyDataChanged(itemCount)
}
private fun rotateCanvas(canvas: Canvas) {
if (mIndicatorOptions.orientation == IndicatorOrientation.INDICATOR_VERTICAL) {
canvas.rotate(90f, width / 2f, width / 2f)
} else if (mIndicatorOptions.orientation == IndicatorOrientation.INDICATOR_RTL) {
canvas.rotate(180f, width / 2f, height / 2f)
}
}
fun setOrientation(@AIndicatorOrientation orientation: Int) {
mIndicatorOptions.orientation = orientation;
}
}

View File

@@ -0,0 +1,17 @@
package com.mogo.eagle.core.widget.indicator.annotation
import androidx.annotation.IntDef
import com.mogo.eagle.core.widget.indicator.enums.IndicatorOrientation
/**
*
* @author zhangpan
* @date 2021/1/21
*/
@IntDef(
IndicatorOrientation.INDICATOR_HORIZONTAL, IndicatorOrientation.INDICATOR_VERTICAL,
IndicatorOrientation.INDICATOR_RTL
)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD)
annotation class AIndicatorOrientation()

View File

@@ -0,0 +1,19 @@
package com.mogo.eagle.core.widget.indicator.annotation
import androidx.annotation.IntDef
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode.Companion.COLOR
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode.Companion.NORMAL
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode.Companion.SCALE
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode.Companion.SMOOTH
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode.Companion.WORM
/**
* <pre>
* Created by zhangpan on 2019-10-18.
* Description:
</pre> *
*/
@IntDef(NORMAL, SMOOTH, WORM, COLOR, SCALE)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD)
annotation class AIndicatorSlideMode

View File

@@ -0,0 +1,15 @@
package com.mogo.eagle.core.widget.indicator.annotation
import androidx.annotation.IntDef
import com.mogo.eagle.core.widget.indicator.enums.IndicatorStyle
/**
* <pre>
* Created by zhangpan on 2019-10-18.
* Description:
</pre> *
*/
@IntDef(IndicatorStyle.CIRCLE, IndicatorStyle.DASH, IndicatorStyle.ROUND_RECT)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD)
annotation class AIndicatorStyle

View File

@@ -0,0 +1,204 @@
package com.mogo.eagle.core.widget.indicator.base
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.annotation.ColorInt
import androidx.recyclerview.widget.RecyclerView
import com.mogo.eagle.core.widget.indicator.annotation.AIndicatorSlideMode
import com.mogo.eagle.core.widget.indicator.annotation.AIndicatorStyle
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhangpan on 2019-09-04.
* Description:IndicatorView基类处理了页面滑动。
* </pre>
*/
@Suppress("UNUSED")
open class BaseIndicatorView constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int
) : View(context, attrs, defStyleAttr), IIndicator {
var mIndicatorOptions: IndicatorOptions
private var recyclerView: RecyclerView? = null
init {
mIndicatorOptions = IndicatorOptions()
}
fun setPageSize(pageSize: Int): BaseIndicatorView {
mIndicatorOptions.pageSize = pageSize
return this
}
// 页面选定
fun onPageSelected(position: Int) {
if (getSlideMode() == IndicatorSlideMode.NORMAL) {
setCurrentPosition(position)
setSlideProgress(0f)
invalidate()
}
}
// 滚动距离
fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
if (getSlideMode() != IndicatorSlideMode.NORMAL && getPageSize() > 1) {
scrollSlider(position, positionOffset)
invalidate()
}
}
private fun scrollSlider(position: Int, positionOffset: Float) {
if (mIndicatorOptions.slideMode == IndicatorSlideMode.SCALE
|| mIndicatorOptions.slideMode == IndicatorSlideMode.COLOR) {
setCurrentPosition(position)
setSlideProgress(positionOffset)
} else {
if (position % getPageSize() == getPageSize() - 1) { // 最后一个页面与第一个页面
if (positionOffset < 0.5) {
setCurrentPosition(position)
setSlideProgress(0f)
} else {
setCurrentPosition(0)
setSlideProgress(0f)
}
} else { // 中间页面
setCurrentPosition(position)
setSlideProgress(positionOffset)
}
}
}
override fun notifyDataChanged(itemCount: Int) {
setPageSize(itemCount)
requestLayout()
invalidate()
}
private fun setupViewPager() {
}
fun getNormalSlideWidth(): Float {
return mIndicatorOptions.normalSliderWidth
}
fun setNormalSlideWidth(normalSliderWidth: Float) {
mIndicatorOptions.normalSliderWidth = normalSliderWidth
}
fun getCheckedSlideWidth(): Float {
return mIndicatorOptions.checkedSliderWidth
}
fun setCheckedSlideWidth(checkedSliderWidth: Float) {
mIndicatorOptions.checkedSliderWidth = checkedSliderWidth
}
val checkedSliderWidth: Float
get() = mIndicatorOptions.checkedSliderWidth
fun setCurrentPosition(currentPosition: Int) {
mIndicatorOptions.currentPosition = currentPosition
}
fun getCurrentPosition(): Int {
return mIndicatorOptions.currentPosition
}
fun getIndicatorGap(indicatorGap: Float) {
mIndicatorOptions.sliderGap = indicatorGap
}
fun setIndicatorGap(indicatorGap: Float) {
mIndicatorOptions.sliderGap = indicatorGap
}
fun setCheckedColor(@ColorInt normalColor: Int) {
mIndicatorOptions.checkedSliderColor = normalColor
}
fun getCheckedColor(): Int {
return mIndicatorOptions.checkedSliderColor
}
fun setNormalColor(@ColorInt normalColor: Int) {
mIndicatorOptions.normalSliderColor = normalColor
}
fun getSlideProgress(): Float {
return mIndicatorOptions.slideProgress
}
fun setSlideProgress(slideProgress: Float) {
mIndicatorOptions.slideProgress = slideProgress
}
fun getPageSize(): Int {
return mIndicatorOptions.pageSize
}
fun setSliderColor(
@ColorInt normalColor: Int,
@ColorInt selectedColor: Int,
@ColorInt selectedEndColor: Int
): BaseIndicatorView {
mIndicatorOptions.setSliderColor(normalColor, selectedColor,selectedEndColor)
return this
}
fun setSliderWidth(sliderWidth: Float): BaseIndicatorView {
mIndicatorOptions.setSliderWidth(sliderWidth)
return this
}
fun setSliderWidth(
normalSliderWidth: Float,
selectedSliderWidth: Float
): BaseIndicatorView {
mIndicatorOptions.setSliderWidth(normalSliderWidth, selectedSliderWidth)
return this
}
fun setSliderGap(sliderGap: Float): BaseIndicatorView {
mIndicatorOptions.sliderGap = sliderGap
return this
}
fun getSlideMode(): Int {
return mIndicatorOptions.slideMode
}
fun setSlideMode(@AIndicatorSlideMode slideMode: Int): BaseIndicatorView {
mIndicatorOptions.slideMode = slideMode
return this
}
fun setIndicatorStyle(@AIndicatorStyle indicatorStyle: Int): BaseIndicatorView {
mIndicatorOptions.indicatorStyle = indicatorStyle
return this
}
fun setSliderHeight(sliderHeight: Float): BaseIndicatorView {
mIndicatorOptions.sliderHeight = sliderHeight
return this
}
fun showIndicatorWhenOneItem(showIndicatorWhenOneItem: Boolean) {
mIndicatorOptions.showIndicatorOneItem = showIndicatorWhenOneItem
}
fun onPageScrollStateChanged(state: Int) {
}
override fun setIndicatorOptions(options: IndicatorOptions) {
mIndicatorOptions = options
}
}

View File

@@ -0,0 +1,17 @@
package com.mogo.eagle.core.widget.indicator.base
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhangpan on 2019-09-02.
* Description:
</pre> *
*/
interface IIndicator {
fun notifyDataChanged(itemCount:Int)
fun setIndicatorOptions(options: IndicatorOptions)
}

View File

@@ -0,0 +1,94 @@
package com.mogo.eagle.core.widget.indicator.drawer
import android.animation.ArgbEvaluator
import android.graphics.Paint
import com.mogo.eagle.core.widget.indicator.enums.IndicatorOrientation
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhpan on 2019/11/23.
* Description:
</pre> *
*/
abstract class BaseDrawer internal constructor(internal var mIndicatorOptions: IndicatorOptions) :
IDrawer {
private val mMeasureResult: MeasureResult
internal var maxWidth: Float = 0.toFloat()
internal var minWidth: Float = 0.toFloat()
internal var mPaint: Paint = Paint()
internal var argbEvaluator: ArgbEvaluator? = null
companion object {
const val INDICATOR_PADDING_ADDITION = 6
const val INDICATOR_PADDING = 3
}
protected val isWidthEquals: Boolean
get() = mIndicatorOptions.normalSliderWidth == mIndicatorOptions.checkedSliderWidth
init {
mPaint.isAntiAlias = true
mMeasureResult = MeasureResult()
if (mIndicatorOptions.slideMode == IndicatorSlideMode.SCALE
|| mIndicatorOptions.slideMode == IndicatorSlideMode.COLOR
) {
argbEvaluator = ArgbEvaluator()
}
}
override fun onMeasure(
widthMeasureSpec: Int,
heightMeasureSpec: Int
): MeasureResult {
maxWidth =
mIndicatorOptions.normalSliderWidth.coerceAtLeast(mIndicatorOptions.checkedSliderWidth)
minWidth =
mIndicatorOptions.normalSliderWidth.coerceAtMost(mIndicatorOptions.checkedSliderWidth)
if (mIndicatorOptions.orientation == IndicatorOrientation.INDICATOR_VERTICAL) {
mMeasureResult.setMeasureResult(measureHeight(), measureWidth())
} else {
mMeasureResult.setMeasureResult(measureWidth(), measureHeight())
}
return mMeasureResult
}
protected open fun measureHeight(): Int {
return mIndicatorOptions.sliderHeight.toInt() + INDICATOR_PADDING
}
private fun measureWidth(): Int {
val pageSize = mIndicatorOptions.pageSize
val indicatorGap = mIndicatorOptions.sliderGap
return ((pageSize - 1) * indicatorGap + maxWidth + (pageSize - 1) * minWidth).toInt() + INDICATOR_PADDING_ADDITION
}
override fun onLayout(
changed: Boolean,
left: Int,
top: Int,
right: Int,
bottom: Int
) {
}
inner class MeasureResult {
var measureWidth: Int = 0
internal set
var measureHeight: Int = 0
internal set
internal fun setMeasureResult(
measureWidth: Int,
measureHeight: Int
) {
this.measureWidth = measureWidth
this.measureHeight = measureHeight
}
}
}

View File

@@ -0,0 +1,157 @@
package com.mogo.eagle.core.widget.indicator.drawer
import android.graphics.Canvas
import android.graphics.RectF
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils
/**
* <pre>
* Created by zhpan on 2019/11/23.
* Description: Circle Indicator drawer.
</pre> *
*/
class CircleDrawer internal constructor(indicatorOptions: IndicatorOptions) : BaseDrawer(
indicatorOptions
) {
private val rectF = RectF()
override fun measureHeight(): Int {
return maxWidth.toInt() + INDICATOR_PADDING_ADDITION
}
override fun onDraw(canvas: Canvas) {
val pageSize = mIndicatorOptions.pageSize
if (pageSize > 1 || mIndicatorOptions.showIndicatorOneItem && pageSize == 1) {
drawNormal(canvas)
drawSlider(canvas)
}
}
private fun drawNormal(canvas: Canvas) {
val normalIndicatorWidth = mIndicatorOptions.normalSliderWidth
mPaint.color = mIndicatorOptions.normalSliderColor
for (i in 0 until mIndicatorOptions.pageSize) {
val coordinateX = IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, i)
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
drawCircle(canvas, coordinateX, coordinateY, normalIndicatorWidth / 2)
}
}
private fun drawSlider(canvas: Canvas) {
mPaint.color = mIndicatorOptions.checkedSliderColor
when (mIndicatorOptions.slideMode) {
IndicatorSlideMode.NORMAL, IndicatorSlideMode.SMOOTH -> drawCircleSlider(canvas)
IndicatorSlideMode.WORM -> drawWormSlider(canvas)
IndicatorSlideMode.SCALE -> drawScaleSlider(canvas)
IndicatorSlideMode.COLOR -> drawColor(canvas)
}
}
private fun drawColor(canvas: Canvas) {
val currentPosition = mIndicatorOptions.currentPosition
val slideProgress = mIndicatorOptions.slideProgress
val coordinateX = IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
var evaluate = argbEvaluator?.evaluate(
slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = (evaluate as Int)
drawCircle(canvas, coordinateX, coordinateY, mIndicatorOptions.normalSliderWidth / 2)
// 绘制可循环的ViewPager指示器渐变
evaluate = argbEvaluator?.evaluate(
1 - slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = evaluate as Int
val nextCoordinateX = if (currentPosition == mIndicatorOptions.pageSize - 1) {
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, 0)
} else {
coordinateX + mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
}
drawCircle(canvas, nextCoordinateX, coordinateY, mIndicatorOptions.checkedSliderWidth / 2)
}
private fun drawScaleSlider(canvas: Canvas) {
val currentPosition = mIndicatorOptions.currentPosition
val slideProgress = mIndicatorOptions.slideProgress
val coordinateX = IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
if (slideProgress < 1) {
val evaluate = argbEvaluator?.evaluate(
slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = (evaluate as Int)
val radius =
mIndicatorOptions.checkedSliderWidth / 2 - (mIndicatorOptions.checkedSliderWidth / 2 - mIndicatorOptions.normalSliderWidth / 2) * slideProgress
drawCircle(canvas, coordinateX, coordinateY, radius)
}
if (currentPosition == mIndicatorOptions.pageSize - 1) {
val evaluate = argbEvaluator?.evaluate(
slideProgress, mIndicatorOptions.normalSliderColor, mIndicatorOptions.checkedSliderColor
)
mPaint.color = evaluate as Int
val nextCoordinateX = maxWidth / 2
val nextRadius = minWidth / 2 + (maxWidth / 2 - minWidth / 2) * (slideProgress)
drawCircle(canvas, nextCoordinateX, coordinateY, nextRadius)
} else {
if (slideProgress > 0) {
val evaluate = argbEvaluator?.evaluate(
slideProgress, mIndicatorOptions.normalSliderColor, mIndicatorOptions.checkedSliderColor
)
mPaint.color = evaluate as Int
val nextCoordinateX =
coordinateX + mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
val nextRadius =
mIndicatorOptions.normalSliderWidth / 2 + (mIndicatorOptions.checkedSliderWidth / 2 - mIndicatorOptions.normalSliderWidth / 2) * slideProgress
drawCircle(canvas, nextCoordinateX, coordinateY, nextRadius)
}
}
}
private fun drawCircleSlider(canvas: Canvas) {
val currentPosition = mIndicatorOptions.currentPosition
val startCoordinateX =
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
val endCoordinateX = IndicatorUtils.getCoordinateX(
mIndicatorOptions, maxWidth, (currentPosition + 1) % mIndicatorOptions.pageSize
)
val coordinateX =
startCoordinateX + (endCoordinateX - startCoordinateX) * mIndicatorOptions.slideProgress
val coordinateY = IndicatorUtils.getCoordinateY(maxWidth)
val radius = mIndicatorOptions.checkedSliderWidth / 2
drawCircle(canvas, coordinateX, coordinateY, radius)
}
private fun drawWormSlider(canvas: Canvas) {
val sliderHeight = mIndicatorOptions.normalSliderWidth
val slideProgress = mIndicatorOptions.slideProgress
val currentPosition = mIndicatorOptions.currentPosition
val distance = mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
val startCoordinateX =
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
val left = startCoordinateX + (distance * (slideProgress - 0.5f) * 2.0f).coerceAtLeast(
0f
) - mIndicatorOptions.normalSliderWidth / 2 + INDICATOR_PADDING
val right = startCoordinateX + (distance * slideProgress * 2f).coerceAtMost(
distance
) + mIndicatorOptions.normalSliderWidth / 2 + INDICATOR_PADDING
rectF.set(left, INDICATOR_PADDING.toFloat(), right, sliderHeight + INDICATOR_PADDING)
canvas.drawRoundRect(rectF, sliderHeight, sliderHeight, mPaint)
}
private fun drawCircle(
canvas: Canvas,
coordinateX: Float,
coordinateY: Float,
radius: Float
) {
canvas.drawCircle(
coordinateX + INDICATOR_PADDING, coordinateY + INDICATOR_PADDING, radius, mPaint
)
}
}

View File

@@ -0,0 +1,20 @@
package com.mogo.eagle.core.widget.indicator.drawer
import android.graphics.Canvas
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhpan on 2019/11/23.
* Description: Dash Indicator Drawer.
</pre> *
*/
class DashDrawer internal constructor(indicatorOptions: IndicatorOptions) : RectDrawer(
indicatorOptions
) {
override fun drawDash(canvas: Canvas) {
canvas.drawRect(mRectF, mPaint)
}
}

View File

@@ -0,0 +1,20 @@
package com.mogo.eagle.core.widget.indicator.drawer
import com.mogo.eagle.core.widget.indicator.enums.IndicatorStyle
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhpan on 2019/11/24.
* Description: Indicator Drawer Factory.
</pre> *
*/
internal object DrawerFactory {
fun createDrawer(indicatorOptions: IndicatorOptions): IDrawer {
return when (indicatorOptions.indicatorStyle) {
IndicatorStyle.DASH -> DashDrawer(indicatorOptions)
IndicatorStyle.ROUND_RECT -> RoundRectDrawer(indicatorOptions)
else -> CircleDrawer(indicatorOptions)
}
}
}

View File

@@ -0,0 +1,48 @@
package com.mogo.eagle.core.widget.indicator.drawer
import android.graphics.Canvas
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhpan on 2019/11/23.
* Description: Indicator Drawer Proxy.
</pre> *
*/
class DrawerProxy(indicatorOptions: IndicatorOptions) : IDrawer {
private lateinit var mIDrawer: IDrawer
init {
init(indicatorOptions)
}
private fun init(indicatorOptions: IndicatorOptions) {
mIDrawer = DrawerFactory.createDrawer(indicatorOptions)
}
override fun onLayout(
changed: Boolean,
left: Int,
top: Int,
right: Int,
bottom: Int
) {
}
override fun onMeasure(
widthMeasureSpec: Int,
heightMeasureSpec: Int
): BaseDrawer.MeasureResult {
return mIDrawer.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
override fun onDraw(canvas: Canvas) {
mIDrawer.onDraw(canvas)
}
fun setIndicatorOptions(indicatorOptions: IndicatorOptions) {
init(indicatorOptions)
}
}

View File

@@ -0,0 +1,28 @@
package com.mogo.eagle.core.widget.indicator.drawer
import android.graphics.Canvas
import com.mogo.eagle.core.widget.indicator.drawer.BaseDrawer
/**
* <pre>
* Created by zhpan on 2019/11/23.
* Description:
</pre> *
*/
interface IDrawer {
fun onLayout(
changed: Boolean,
left: Int,
top: Int,
right: Int,
bottom: Int
)
fun onMeasure(
widthMeasureSpec: Int,
heightMeasureSpec: Int
): BaseDrawer.MeasureResult
fun onDraw(canvas: Canvas)
}

View File

@@ -0,0 +1,220 @@
package com.mogo.eagle.core.widget.indicator.drawer
import android.graphics.*
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils
/**
* <pre>
* Created by zhpan on 2020/1/17.
* Description:
</pre> *
*/
open class RectDrawer internal constructor(indicatorOptions: IndicatorOptions) : BaseDrawer(
indicatorOptions
) {
internal var mRectF: RectF = RectF()
override fun onDraw(canvas: Canvas) {
val pageSize = mIndicatorOptions.pageSize
if (pageSize > 1 || mIndicatorOptions.showIndicatorOneItem && pageSize == 1) {
if (isWidthEquals && mIndicatorOptions.slideMode != IndicatorSlideMode.NORMAL) {
drawUncheckedSlider(canvas, pageSize)
drawCheckedSlider(canvas)
} else { // 单独处理normalWidth与checkedWidth不一致的情况
if (mIndicatorOptions.slideMode == IndicatorSlideMode.SCALE) {
for (i in 0 until pageSize) {
drawScaleSlider(canvas, i)
}
} else {
drawInequalitySlider(canvas, pageSize)
}
}
}
}
private fun drawScaleSlider(
canvas: Canvas,
i: Int
) {
val checkedColor = mIndicatorOptions.checkedSliderColor
val indicatorGap = mIndicatorOptions.sliderGap
val sliderHeight = mIndicatorOptions.sliderHeight
val currentPosition = mIndicatorOptions.currentPosition
val normalWidth = mIndicatorOptions.normalSliderWidth
val checkedWidth = mIndicatorOptions.checkedSliderWidth
when {
i < currentPosition -> {
mPaint.color = mIndicatorOptions.normalSliderColor
val left: Float = if (currentPosition == mIndicatorOptions.pageSize - 1) {
(i * normalWidth + i * indicatorGap) + (checkedWidth - normalWidth) * mIndicatorOptions.slideProgress
} else {
(i * normalWidth + i * indicatorGap)
}
mRectF.set(left, 0f, left + normalWidth, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
}
i == currentPosition -> {
mPaint.color = checkedColor
val slideProgress = mIndicatorOptions.slideProgress
if (currentPosition == mIndicatorOptions.pageSize - 1) {
val evaluate = argbEvaluator?.evaluate(
slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = (evaluate as Int)
val right =
(mIndicatorOptions.pageSize - 1) * (normalWidth + mIndicatorOptions.sliderGap) + checkedWidth
val left = right - checkedWidth + (checkedWidth - normalWidth) * (slideProgress)
mRectF.set(left, 0f, right, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
} else {
if (slideProgress < 1) {
val evaluate = argbEvaluator?.evaluate(
slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = (evaluate as Int)
val left = i * normalWidth + i * indicatorGap
val right = left + normalWidth + (checkedWidth - normalWidth) * (1 - slideProgress)
mRectF.set(left, 0f, right, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
}
}
if (currentPosition == mIndicatorOptions.pageSize - 1) {
if (slideProgress > 0) {
val evaluate = argbEvaluator?.evaluate(
1 - slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = evaluate as Int
val left = 0f
val right = left + normalWidth + (checkedWidth - normalWidth) * slideProgress
mRectF.set(left, 0f, right, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
}
} else {
if (slideProgress > 0) {
val evaluate = argbEvaluator?.evaluate(
1 - slideProgress, checkedColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = evaluate as Int
val right =
i * normalWidth + i * indicatorGap + normalWidth + (indicatorGap + checkedWidth)
val left = right - (normalWidth) - (checkedWidth - normalWidth) * (slideProgress)
mRectF.set(left, 0f, right, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,mRectF.width()>checkedWidth/2)
}
}
}
else -> {
if ((currentPosition + 1 != i || mIndicatorOptions.slideProgress == 0f)) { // 避免多余绘制
mPaint.color = mIndicatorOptions.normalSliderColor
val left = i * minWidth + i * indicatorGap + (checkedWidth - minWidth)
mRectF.set(left, 0f, left + minWidth, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,false)
}
}
}
}
private fun drawUncheckedSlider(
canvas: Canvas,
pageSize: Int
) {
for (i in 0 until pageSize) {
mPaint.color = mIndicatorOptions.normalSliderColor
val left = i * maxWidth + i * +mIndicatorOptions.sliderGap + (maxWidth - minWidth)
mRectF.set(left, 0f, left + minWidth, mIndicatorOptions.sliderHeight)
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
}
}
private fun drawInequalitySlider(
canvas: Canvas,
pageSize: Int
) {
var left = 0f
for (i in 0 until pageSize) {
val sliderWidth = (if (i == mIndicatorOptions.currentPosition) maxWidth else minWidth)
mPaint.color =
if (i == mIndicatorOptions.currentPosition) mIndicatorOptions.checkedSliderColor else mIndicatorOptions.normalSliderColor
mRectF.set(left, 0f, left + sliderWidth, mIndicatorOptions.sliderHeight)
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
left += sliderWidth + mIndicatorOptions.sliderGap
}
}
private fun drawCheckedSlider(canvas: Canvas) {
mPaint.color = mIndicatorOptions.checkedSliderColor
when (mIndicatorOptions.slideMode) {
IndicatorSlideMode.SMOOTH -> drawSmoothSlider(canvas)
IndicatorSlideMode.WORM -> drawWormSlider(canvas)
IndicatorSlideMode.COLOR -> drawColorSlider(canvas)
}
}
private fun drawColorSlider(canvas: Canvas) {
val currentPosition = mIndicatorOptions.currentPosition
val slideProgress = mIndicatorOptions.slideProgress
val left = currentPosition * minWidth + currentPosition * mIndicatorOptions.sliderGap
if (slideProgress < 0.99) {
val evaluate = argbEvaluator?.evaluate(
slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = (evaluate as Int)
mRectF.set(left, 0f, left + minWidth, mIndicatorOptions.sliderHeight)
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
}
var nextSliderLeft = left + mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
if (currentPosition == mIndicatorOptions.pageSize - 1) {
nextSliderLeft = 0f
}
val evaluate = argbEvaluator?.evaluate(
1 - slideProgress, mIndicatorOptions.checkedSliderColor, mIndicatorOptions.normalSliderColor
)
mPaint.color = evaluate as Int
mRectF.set(nextSliderLeft, 0f, nextSliderLeft + minWidth, mIndicatorOptions.sliderHeight)
drawRoundRect(canvas, mIndicatorOptions.sliderHeight, mIndicatorOptions.sliderHeight,false)
}
private fun drawWormSlider(canvas: Canvas) {
val sliderHeight = mIndicatorOptions.sliderHeight
val slideProgress = mIndicatorOptions.slideProgress
val currentPosition = mIndicatorOptions.currentPosition
val distance = mIndicatorOptions.sliderGap + mIndicatorOptions.normalSliderWidth
val startCoordinateX =
IndicatorUtils.getCoordinateX(mIndicatorOptions, maxWidth, currentPosition)
val left = startCoordinateX + (distance * (slideProgress - 0.5f) * 2.0f).coerceAtLeast(
0f
) - mIndicatorOptions.normalSliderWidth / 2
val right = startCoordinateX + (distance * slideProgress * 2f).coerceAtMost(
distance
) + mIndicatorOptions.normalSliderWidth / 2
mRectF.set(left, 0f, right, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,false)
}
private fun drawSmoothSlider(canvas: Canvas) {
val currentPosition = mIndicatorOptions.currentPosition
val indicatorGap = mIndicatorOptions.sliderGap
val sliderHeight = mIndicatorOptions.sliderHeight
val left =
currentPosition * maxWidth + currentPosition * +indicatorGap + (maxWidth + indicatorGap) * mIndicatorOptions.slideProgress
mRectF.set(left, 0f, left + maxWidth, sliderHeight)
drawRoundRect(canvas, sliderHeight, sliderHeight,false)
}
protected open fun drawRoundRect(
canvas: Canvas,
rx: Float,
ry: Float,
isWidth:Boolean
) {
drawDash(canvas)
}
protected open fun drawDash(canvas: Canvas) {}
}

View File

@@ -0,0 +1,38 @@
package com.mogo.eagle.core.widget.indicator.drawer
import android.graphics.Canvas
import android.graphics.LinearGradient
import android.graphics.Shader
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhpan on 2019/11/26.
* Description:
</pre> *
*/
class RoundRectDrawer internal constructor(val indicatorOptions: IndicatorOptions) : RectDrawer(
indicatorOptions
) {
override fun drawRoundRect(canvas: Canvas, rx: Float, ry: Float, isWidth: Boolean) {
if(isWidth) {
val linearGradient =
LinearGradient(
mRectF.left,
(mRectF.bottom - mRectF.top) / 2,
mRectF.right,
(mRectF.bottom - mRectF.top) / 2,
indicatorOptions.checkedSliderColor,
indicatorOptions.checkedEndSliderColor,
Shader.TileMode.CLAMP
)
mPaint.shader = linearGradient
}else{
mPaint.color = indicatorOptions.normalSliderColor
mPaint.shader = null
}
canvas.drawRoundRect(mRectF, rx, ry, mPaint)
}
}

View File

@@ -0,0 +1,16 @@
package com.mogo.eagle.core.widget.indicator.enums
import android.widget.LinearLayout
/**
*
* @author zhangpan
* @date 2021/1/21
*/
class IndicatorOrientation {
companion object {
const val INDICATOR_HORIZONTAL = LinearLayout.HORIZONTAL
const val INDICATOR_VERTICAL = LinearLayout.VERTICAL
const val INDICATOR_RTL = 3
}
}

View File

@@ -0,0 +1,17 @@
package com.mogo.eagle.core.widget.indicator.enums
/**
* <pre>
* Created by zhangpan on 2019-10-18.
* Description:
</pre> *
*/
interface IndicatorSlideMode {
companion object {
const val NORMAL = 0
const val SMOOTH = 2
const val WORM = 3
const val SCALE = 4
const val COLOR = 5
}
}

View File

@@ -0,0 +1,15 @@
package com.mogo.eagle.core.widget.indicator.enums
/**
* <pre>
* Created by zhangpan on 2019-10-18.
* Description:
</pre> *
*/
interface IndicatorStyle {
companion object {
const val CIRCLE = 0
const val DASH = 1 shl 1
const val ROUND_RECT = 1 shl 2
}
}

View File

@@ -0,0 +1,38 @@
package com.mogo.eagle.core.widget.indicator.option;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.widget.R;
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils;
public class AttrsController {
public static void initAttrs(@NonNull Context context, @Nullable AttributeSet attrs,
IndicatorOptions indicatorOptions) {
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IndicatorView);
int indicatorSlideMode = typedArray.getInt(R.styleable.IndicatorView_vpi_slide_mode, 0);
int indicatorStyle = typedArray.getInt(R.styleable.IndicatorView_vpi_style, 0);
int checkedColor = typedArray.getColor(R.styleable.IndicatorView_vpi_slider_checked_color,
Color.parseColor("#6C6D72"));
int normalColor = typedArray.getColor(R.styleable.IndicatorView_vpi_slider_normal_color,
Color.parseColor("#8C18171C"));
int orientation = typedArray.getInt(R.styleable.IndicatorView_vpi_orientation, 0);
float radius = typedArray.getDimension(R.styleable.IndicatorView_vpi_slider_radius,
IndicatorUtils.dp2px(8));
indicatorOptions.setCheckedColor(checkedColor);
indicatorOptions.setNormalSliderColor(normalColor);
indicatorOptions.setOrientation(orientation);
indicatorOptions.setIndicatorStyle(indicatorStyle);
indicatorOptions.setSlideMode(indicatorSlideMode);
indicatorOptions.setSliderWidth(radius * 2, radius * 2);
typedArray.recycle();
}
}
}

View File

@@ -0,0 +1,114 @@
package com.mogo.eagle.core.widget.indicator.option
import android.graphics.Color
import com.mogo.eagle.core.widget.indicator.annotation.AIndicatorOrientation
import com.mogo.eagle.core.widget.indicator.annotation.AIndicatorSlideMode
import com.mogo.eagle.core.widget.indicator.annotation.AIndicatorStyle
import com.mogo.eagle.core.widget.indicator.enums.IndicatorOrientation
import com.mogo.eagle.core.widget.indicator.enums.IndicatorSlideMode
import com.mogo.och.taxi.passenger.widget.indicator.utils.IndicatorUtils
/**
* <pre>
* Created by zhpan on 2019/11/20.
* Description:Indicator的配置参数
</pre> *
*/
class IndicatorOptions {
@AIndicatorOrientation
var orientation = IndicatorOrientation.INDICATOR_HORIZONTAL
@AIndicatorStyle
var indicatorStyle: Int = 0
/**
* Indicator滑动模式目前仅支持两种
*
* @see IndicatorSlideMode.NORMAL
*
* @see IndicatorSlideMode.SMOOTH
*/
@AIndicatorSlideMode
var slideMode: Int = 0
/**
* 页面size
*/
var pageSize: Int = 0
/**
* 未选中时Indicator颜色
*/
var normalSliderColor: Int = 0
/**
* 选中时Indicator颜色
*/
var checkedSliderColor: Int = 0
/**
* 选中时IndicatorEnd颜色
*/
var checkedEndSliderColor: Int = 0
/**
* Indicator间距
*/
var sliderGap: Float = 0f
var sliderHeight: Float = 0f
get() = if (field > 0) field else normalSliderWidth / 2
var normalSliderWidth: Float = 0f
var checkedSliderWidth: Float = 0f
/**
* 指示器当前位置
*/
var currentPosition: Int = 0
/**
* 从一个点滑动到另一个点的进度
*/
var slideProgress: Float = 0f
var showIndicatorOneItem: Boolean = false
init {
normalSliderWidth = IndicatorUtils.dp2px(8f)
.toFloat()
checkedSliderWidth = normalSliderWidth
sliderGap = normalSliderWidth
normalSliderColor = Color.parseColor("#8C18171C")
checkedSliderColor = Color.parseColor("#8C6C6D72")
slideMode = IndicatorSlideMode.NORMAL
}
fun setCheckedColor(checkedColor: Int) {
this.checkedSliderColor = checkedColor
}
fun setSliderWidth(
normalIndicatorWidth: Float,
checkedIndicatorWidth: Float
) {
this.normalSliderWidth = normalIndicatorWidth
this.checkedSliderWidth = checkedIndicatorWidth
}
fun setSliderWidth(sliderWidth: Float) {
setSliderWidth(sliderWidth, sliderWidth)
}
fun setSliderColor(
normalColor: Int,
checkedColor: Int,
selectedEndColor: Int
) {
this.normalSliderColor = normalColor
this.checkedSliderColor = checkedColor
this.checkedEndSliderColor = selectedEndColor
}
}

View File

@@ -0,0 +1,32 @@
package com.mogo.och.taxi.passenger.widget.indicator.utils
import android.content.res.Resources
import com.mogo.eagle.core.widget.indicator.option.IndicatorOptions
/**
* <pre>
* Created by zhangpan on 2020-01-20.
* Description:
</pre> *
*/
object IndicatorUtils {
@JvmStatic
fun dp2px(dpValue: Float): Int {
return (0.5f + dpValue * Resources.getSystem().displayMetrics.density).toInt()
}
fun getCoordinateX(
indicatorOptions: IndicatorOptions,
maxDiameter: Float,
index: Int
): Float {
val normalIndicatorWidth = indicatorOptions.normalSliderWidth
return maxDiameter / 2 + (normalIndicatorWidth + indicatorOptions.sliderGap) * index
}
fun getCoordinateY(maxDiameter: Float): Float {
return maxDiameter / 2
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="IndicatorView">
<attr name="vpi_slider_checked_color" format="color" />
<attr name="vpi_slider_normal_color" format="color" />
<attr name="vpi_slider_radius" format="dimension" />
<attr name="vpi_rtl" format="boolean" />
<attr name="vpi_orientation" format="enum">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
<enum name="rtl" value="3"/>
</attr>
<attr name="vpi_slide_mode" format="enum">
<enum name="normal" value="0" />
<enum name="smooth" value="2" />
<enum name="worm" value="3" />
<enum name="scale" value="4" />
<enum name="color" value="5" />
</attr>
<attr name="vpi_style" format="enum">
<enum name="circle" value="0" />
<enum name="dash" value="2" />
<enum name="round_rect" value="4" />
</attr>
</declare-styleable>
</resources>