修改交友布局
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
|
||||
public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {
|
||||
|
||||
public enum State {
|
||||
EXPANDED,
|
||||
COLLAPSED,
|
||||
IDLE
|
||||
}
|
||||
|
||||
private State mCurrentState = State.IDLE;
|
||||
|
||||
@Override
|
||||
public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
|
||||
if (i == 0) {
|
||||
if (mCurrentState != State.EXPANDED) {
|
||||
onStateChanged(appBarLayout, State.EXPANDED);
|
||||
}
|
||||
mCurrentState = State.EXPANDED;
|
||||
} else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
|
||||
if (mCurrentState != State.COLLAPSED) {
|
||||
onStateChanged(appBarLayout, State.COLLAPSED);
|
||||
}
|
||||
mCurrentState = State.COLLAPSED;
|
||||
} else {
|
||||
if (mCurrentState != State.IDLE) {
|
||||
onStateChanged(appBarLayout, State.IDLE);
|
||||
}
|
||||
mCurrentState = State.IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void onStateChanged(AppBarLayout appBarLayout, State state);
|
||||
}
|
||||
@@ -0,0 +1,883 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.blankj.utilcode.util.LogUtils;
|
||||
import com.blankj.utilcode.util.PathUtils;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.liulishuo.okdownload.DownloadTask;
|
||||
import com.liulishuo.okdownload.core.cause.EndCause;
|
||||
import com.liulishuo.okdownload.core.cause.ResumeFailedCause;
|
||||
import com.liulishuo.okdownload.core.listener.DownloadListener1;
|
||||
import com.liulishuo.okdownload.core.listener.assist.Listener1Assist;
|
||||
import com.opensource.svgaplayer.SVGACallback;
|
||||
import com.opensource.svgaplayer.SVGADrawable;
|
||||
import com.opensource.svgaplayer.SVGADynamicEntity;
|
||||
import com.opensource.svgaplayer.SVGAImageView;
|
||||
import com.opensource.svgaplayer.SVGAParser;
|
||||
import com.opensource.svgaplayer.SVGAVideoEntity;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.databinding.RoomViewSvgaAnimBinding;
|
||||
import com.xscm.moduleutil.utils.Md5Utils;
|
||||
import com.xscm.moduleutil.utils.MemoryOptimizationUtils;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
import com.xscm.moduleutil.utils.logger.Logger;
|
||||
import com.tencent.qgame.animplayer.AnimConfig;
|
||||
import com.tencent.qgame.animplayer.inter.IAnimListener;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.URL;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
|
||||
public class AvatarFrameView extends FrameLayout implements IAnimListener {
|
||||
|
||||
@Override
|
||||
public void onFailed(int i, @Nullable String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onVideoConfigReady(@NonNull AnimConfig animConfig) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoStart() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRender(int i, @Nullable AnimConfig animConfig) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoComplete() {
|
||||
// if (mType == 1) {
|
||||
// mBinding.playView.startPlay(mFile); // 循环播放
|
||||
// } else {
|
||||
// isPlaying = false;
|
||||
// playNextFromQueue(); // 播放下一项
|
||||
// }
|
||||
// if (isDestroyed) return;
|
||||
|
||||
// 确保在主线程中执行
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
handleVideoComplete();
|
||||
} else {
|
||||
mainHandler.post(() -> {
|
||||
// if (!isDestroyed) {
|
||||
handleVideoComplete();
|
||||
// }
|
||||
});
|
||||
}
|
||||
}
|
||||
private void handleVideoComplete() {
|
||||
// if (isDestroyed) return;
|
||||
|
||||
if (mType == 1) {
|
||||
if (mBinding != null && mFile != null) {
|
||||
mBinding.playView.startPlay(mFile); // 循环播放
|
||||
}
|
||||
} else {
|
||||
isPlaying = false;
|
||||
// playNextFromQueue(); // 播放下一项
|
||||
mainHandler.postDelayed(this::playNextFromQueue, 50);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onVideoDestroy() {
|
||||
|
||||
}
|
||||
|
||||
public enum RenderType {SVGA, MP4}
|
||||
|
||||
private RenderType renderType;
|
||||
private ExoPlayer exoPlayer;
|
||||
private PlayerView playerView;
|
||||
private SVGAImageView svgaSurface;
|
||||
private GLSurfaceView glSurfaceView;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private ChannelSplitRenderer1 renderer;
|
||||
private int mType;//1:循环播放 2:播放一次停止播放
|
||||
private File mFile;
|
||||
private final Queue<PlayItem> playQueue = new LinkedList<>();
|
||||
|
||||
private boolean isPlaying = false;
|
||||
// 添加销毁标记
|
||||
private boolean isDestroyed = false;
|
||||
private static final String TAG = "AvatarFrameView";
|
||||
private RoomViewSvgaAnimBinding mBinding;
|
||||
|
||||
// 内存监控
|
||||
private static final long MAX_MEMORY_THRESHOLD = 300 * 1024 * 1024; // 300MB
|
||||
private static final int MAX_SVGA_CACHE_SIZE = 3;
|
||||
private final Map<String, WeakReference<SVGAVideoEntity>> svgaCache = new LruCache<>(MAX_SVGA_CACHE_SIZE);
|
||||
|
||||
|
||||
public AvatarFrameView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public AvatarFrameView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public AvatarFrameView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_view_svga_anim, this, true);
|
||||
initViews();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
// if (isDestroyed) return;
|
||||
// 初始化 ExoPlayer View
|
||||
playerView = new PlayerView(getContext());
|
||||
playerView.setUseController(false);
|
||||
playerView.setVisibility(View.GONE);
|
||||
addView(playerView);
|
||||
|
||||
// 初始化 SVGA View
|
||||
svgaSurface = new SVGAImageView(getContext());
|
||||
svgaSurface.setVisibility(View.GONE);
|
||||
addView(svgaSurface);
|
||||
|
||||
// // 初始化 GLSurfaceView
|
||||
// glSurfaceView = new GLSurfaceView(getContext());
|
||||
// glSurfaceView.setEGLContextClientVersion(2);
|
||||
// renderer = new ChannelSplitRenderer1();
|
||||
// glSurfaceView.setRenderer(renderer);
|
||||
// glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
|
||||
// glSurfaceView.setVisibility(View.GONE); // 默认隐藏
|
||||
// addView(glSurfaceView);
|
||||
|
||||
// 初始化 ExoPlayer
|
||||
// if (!isDestroyed) {
|
||||
try {
|
||||
exoPlayer = new ExoPlayer.Builder(getContext()).build();
|
||||
playerView.setPlayer(exoPlayer);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e("AvatarFrameView", "Failed to initialize ExoPlayer: " + e.getMessage());
|
||||
}
|
||||
// }
|
||||
|
||||
if (mBinding != null) {
|
||||
mBinding.playView.setAnimListener(this);
|
||||
}
|
||||
}
|
||||
private String getFileExtension(String url) {
|
||||
if (url == null || url.isEmpty()) return "";
|
||||
int dotIndex = url.lastIndexOf(".");
|
||||
if (dotIndex > 0 && dotIndex < url.length() - 1) {
|
||||
return url.substring(dotIndex + 1).toLowerCase(); // 返回 "mp4", "svga" 等
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private void playNextFromQueue() {
|
||||
// if (isDestroyed) return;
|
||||
// 确保在主线程中执行
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(this::playNextFromQueue);
|
||||
return;
|
||||
}
|
||||
// 再次检查内存状态
|
||||
// if (isMemoryLow()) {
|
||||
// LogUtils.w(TAG, "Low memory, clearing queue");
|
||||
// clearQueue();
|
||||
// return;
|
||||
// }
|
||||
// 检查特效是否开启
|
||||
if (SpUtil.getOpenEffect() != 1) {
|
||||
clearQueue();
|
||||
return;
|
||||
}
|
||||
// 关键:即使 isPlaying 为 true,也要检查实际播放状态
|
||||
if (isPlaying && isActuallyPlaying()) {
|
||||
Logger.d("AvatarFrameView", "Already playing, skip");
|
||||
return;
|
||||
}
|
||||
PlayItem item = playQueue.poll();
|
||||
if (item != null) {
|
||||
isPlaying = true;
|
||||
Logger.d("AvatarFrameView", "Playing item, remaining queue size: " + playQueue.size());
|
||||
RenderType type = null;
|
||||
String ext = getFileExtension(item.url);
|
||||
if ("svga".equalsIgnoreCase(ext)) {
|
||||
type = RenderType.SVGA;
|
||||
} else if ("mp4".equalsIgnoreCase(ext)) {
|
||||
type = RenderType.MP4;
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
isPlaying = false;
|
||||
playNextFromQueue(); // 跳过无效项
|
||||
return;
|
||||
}
|
||||
|
||||
clearPrevious();
|
||||
renderType = type;
|
||||
mType = item.type;
|
||||
|
||||
switch (type) {
|
||||
case SVGA:
|
||||
mBinding.playView.stopPlay();
|
||||
mBinding.playView.setVisibility(View.GONE);
|
||||
loadSVGA(item.url);
|
||||
break;
|
||||
case MP4:
|
||||
mBinding.playView.setVisibility(View.VISIBLE);
|
||||
downloadAndPlayMp4(item.url);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
isPlaying = false;
|
||||
Logger.d("AvatarFrameView", "Queue is empty, stop playing");
|
||||
}
|
||||
}
|
||||
// 添加实际播放状态检查方法
|
||||
private boolean isActuallyPlaying() {
|
||||
if (renderType == RenderType.SVGA && svgaSurface != null) {
|
||||
return svgaSurface.isAnimating();
|
||||
} else if (renderType == RenderType.MP4 && mBinding != null) {
|
||||
// 检查播放器状态
|
||||
return mBinding.playView.isRunning();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSource(String url, int type2) {
|
||||
// if (isDestroyed) return;
|
||||
// 确保在主线程中执行
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(() -> setSource(url, type2));
|
||||
return;
|
||||
}
|
||||
// // 检查内存状态
|
||||
// if (isMemoryLow()) {
|
||||
// LogUtils.w(TAG, "Low memory, skipping animation");
|
||||
// clearQueue();
|
||||
// return;
|
||||
// }
|
||||
// 检查特效是否开启
|
||||
if (SpUtil.getOpenEffect() != 1) {
|
||||
// 特效关闭时清空队列并停止播放
|
||||
clearQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加到播放队列
|
||||
// playQueue.offer(new PlayItem(url, type2));
|
||||
playQueue.add(new PlayItem(url, type2));
|
||||
Logger.d("AvatarFrameView", "Added to queue, queue size: " + playQueue.size() + ", url: " + url);
|
||||
|
||||
// 如果当前没有在播放,则开始播放
|
||||
// if (!isPlaying) {
|
||||
// playNextFromQueue();
|
||||
// }
|
||||
// 改进播放检查逻辑
|
||||
checkAndStartPlayback();
|
||||
}
|
||||
|
||||
|
||||
private void checkAndStartPlayback() {
|
||||
// 如果队列不为空且当前没有在播放,则开始播放
|
||||
if (!playQueue.isEmpty() && (!isPlaying || !isActuallyPlaying())) {
|
||||
playNextFromQueue();
|
||||
}
|
||||
}
|
||||
private boolean isMemoryLow() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
||||
long maxMemory = runtime.maxMemory();
|
||||
double memoryUsage = (double) usedMemory / maxMemory;
|
||||
|
||||
// 内存使用超过80%或绝对使用量超过阈值
|
||||
return memoryUsage > 0.8 || usedMemory > MAX_MEMORY_THRESHOLD;
|
||||
}
|
||||
|
||||
// public void setSource(String url, int type2) {
|
||||
// if (SpUtil.getOpenEffect()==1) {
|
||||
// playQueue.offer(new PlayItem(url, type2));
|
||||
// if (!isPlaying) {
|
||||
// playNextFromQueue();
|
||||
// }
|
||||
// }else {
|
||||
// playQueue.clear();
|
||||
// isPlaying = false;
|
||||
// }
|
||||
//
|
||||
//// RenderType type = null;
|
||||
//// if ("svga".equalsIgnoreCase(getFileExtension(url))){
|
||||
//// type = RenderType.SVGA;
|
||||
//// }else if ("mp4".equalsIgnoreCase(getFileExtension(url))){
|
||||
//// type = RenderType.MP4;
|
||||
//// }
|
||||
////
|
||||
//// clearPrevious();
|
||||
//// renderType = type;
|
||||
//// mType = type2;
|
||||
//// switch (type) {
|
||||
//// case SVGA:
|
||||
//// mBinding.playView.stopPlay();
|
||||
//// mBinding.playView.setVisibility(View.GONE);
|
||||
//// loadSVGA(url);
|
||||
//// break;
|
||||
//// case MP4:
|
||||
////// loadMP4(url);
|
||||
//// mBinding.playView.setVisibility(View.VISIBLE);
|
||||
//// downloadAndPlayMp4(url);
|
||||
//// break;
|
||||
//// }
|
||||
// }
|
||||
|
||||
private void downloadAndPlayMp4(String url) {
|
||||
String filePath = PathUtils.getInternalAppCachePath() + Md5Utils.getStringMD5(url) + ".mp4";
|
||||
File file = new File(filePath);
|
||||
|
||||
if (file.exists()) {
|
||||
playMp4(file);
|
||||
mFile = file;
|
||||
} else {
|
||||
DownloadTask task = new DownloadTask.Builder(url, PathUtils.getInternalAppCachePath()
|
||||
, Md5Utils.getStringMD5(url) + ".mp4")
|
||||
.setMinIntervalMillisCallbackProcess(100)
|
||||
.setPassIfAlreadyCompleted(false)
|
||||
.setAutoCallbackToUIThread(true)
|
||||
.build();
|
||||
// if (StatusUtil.isCompleted(task)) {
|
||||
// playMp4(task.getFile());
|
||||
// mFile = task.getFile();
|
||||
// } else {
|
||||
task.enqueue(new DownloadListener1() {
|
||||
@Override
|
||||
public void taskStart(@NonNull DownloadTask task, @NonNull Listener1Assist.Listener1Model model) {
|
||||
Logger.e("taskStart", model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retry(@NonNull DownloadTask task, @NonNull ResumeFailedCause cause) {
|
||||
Logger.e("retry", cause);
|
||||
task.cancel();
|
||||
|
||||
// CrashReport.postCatchedException(new RuntimeException("下载文件重试:" + cause == null ? "" : cause.name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(@NonNull DownloadTask task, int blockCount, long currentOffset, long totalLength) {
|
||||
Logger.e("connected", blockCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(@NonNull DownloadTask task, long currentOffset, long totalLength) {
|
||||
Logger.e("progress", currentOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskEnd(@NonNull DownloadTask task, @NonNull EndCause cause, @Nullable Exception realCause, @NonNull Listener1Assist.Listener1Model model) {
|
||||
Logger.e("taskEnd", model);
|
||||
playMp4(task.getFile());
|
||||
mFile = task.getFile();
|
||||
if (cause != null && cause != EndCause.COMPLETED) {
|
||||
// CrashReport.postCatchedException(new RuntimeException("下载任务结束:" + cause == null ? "" : cause.name() + "_realCause:" + realCause == null ? "" : realCause.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private void playMp4(File file) {
|
||||
if (file != null) {
|
||||
mBinding.playView.startPlay(file);
|
||||
|
||||
} else {
|
||||
// showAnim();
|
||||
// playMp4(file);
|
||||
// CrashReport.postCatchedException(new RuntimeException("播放MP4失败:File is null"));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSVGAComplete(SVGAVideoEntity videoItem, String url) {
|
||||
// if (isDestroyed || svgaSurface == null) return;
|
||||
|
||||
try {
|
||||
// 缓存实体(使用弱引用)
|
||||
svgaCache.put(url, new WeakReference<>(videoItem));
|
||||
|
||||
SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity());
|
||||
svgaSurface.setImageDrawable(drawable);
|
||||
svgaSurface.setCallback(new SVGACallback() {
|
||||
@Override
|
||||
public void onStep(int i, double v) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeat() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished() {
|
||||
// if (isDestroyed) return;
|
||||
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(() -> {
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
});
|
||||
} else {
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
}
|
||||
}
|
||||
});
|
||||
svgaSurface.startAnimation();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error handling SVGA completion: " + e.getMessage());
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private void playCachedSVGA(SVGAVideoEntity videoItem) {
|
||||
// if (isDestroyed || svgaSurface == null) return;
|
||||
|
||||
try {
|
||||
SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity());
|
||||
svgaSurface.setImageDrawable(drawable);
|
||||
svgaSurface.setCallback(new SVGACallback() {
|
||||
@Override
|
||||
public void onStep(int i, double v) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeat() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished() {
|
||||
// if (isDestroyed) return;
|
||||
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(() -> {
|
||||
isPlaying = false;
|
||||
// 添加延迟确保状态更新
|
||||
mainHandler.postDelayed(AvatarFrameView.this::playNextFromQueue, 50);
|
||||
// playNextFromQueue();
|
||||
});
|
||||
} else {
|
||||
isPlaying = false;
|
||||
// playNextFromQueue();
|
||||
mainHandler.postDelayed(AvatarFrameView.this::playNextFromQueue, 50);
|
||||
}
|
||||
}
|
||||
});
|
||||
svgaSurface.startAnimation();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error playing cached SVGA: " + e.getMessage());
|
||||
isPlaying = false;
|
||||
mainHandler.postDelayed(AvatarFrameView.this::playNextFromQueue, 50);
|
||||
// playNextFromQueue();
|
||||
}
|
||||
}
|
||||
private void loadNewSVGA(String url) {
|
||||
// if (isDestroyed) return;
|
||||
|
||||
try {
|
||||
new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() {
|
||||
@Override
|
||||
public void onComplete(SVGAVideoEntity videoItem) {
|
||||
// if (isDestroyed) return;
|
||||
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(() -> handleSVGAComplete(videoItem, url));
|
||||
} else {
|
||||
handleSVGAComplete(videoItem, url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
// if (isDestroyed) return;
|
||||
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(() -> {
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
});
|
||||
} else {
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error parsing SVGA: " + e.getMessage());
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
}
|
||||
}
|
||||
private void loadSVGA(String url) {
|
||||
// if (isDestroyed || svgaSurface == null) return;
|
||||
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(() -> loadSVGA(url));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
svgaSurface.setVisibility(View.VISIBLE);
|
||||
|
||||
// 检查缓存
|
||||
WeakReference<SVGAVideoEntity> cachedRef = svgaCache.get(url);
|
||||
SVGAVideoEntity cachedEntity = cachedRef != null ? cachedRef.get() : null;
|
||||
|
||||
if (cachedEntity != null) {
|
||||
// 使用缓存的实体
|
||||
playCachedSVGA(cachedEntity);
|
||||
} else {
|
||||
// 加载新的SVGA
|
||||
loadNewSVGA(url);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error loading SVGA: " + e.getMessage());
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
}
|
||||
|
||||
svgaSurface.setVisibility(View.VISIBLE);
|
||||
try {
|
||||
new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() {
|
||||
@Override
|
||||
public void onComplete(SVGAVideoEntity videoItem) {
|
||||
SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity());
|
||||
svgaSurface.setImageDrawable(drawable);
|
||||
svgaSurface.setCallback(new SVGACallback() {
|
||||
|
||||
@Override
|
||||
public void onStep(int i, double v) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeat() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished() {
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
}
|
||||
});
|
||||
|
||||
// svgaSurface.setCallback(new SVGAImageViewCallback() {
|
||||
// @Override
|
||||
// public void onAnimationFinished() {
|
||||
// isPlaying = false;
|
||||
// playNextFromQueue();
|
||||
// }
|
||||
// });
|
||||
svgaSurface.startAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
isPlaying = false;
|
||||
playNextFromQueue();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadMP4(String url) {
|
||||
svgaSurface.setVisibility(View.GONE);
|
||||
playerView.setVisibility(View.GONE);
|
||||
|
||||
glSurfaceView.setVisibility(View.VISIBLE);
|
||||
glSurfaceView.onResume();
|
||||
glSurfaceView.requestRender();
|
||||
// 使用 post 确保 GLSurfaceView 已完成 layout
|
||||
glSurfaceView.post(() -> {
|
||||
Log.d("@@@", "GLSurfaceView size after layout: " + glSurfaceView.getWidth() + "x" + glSurfaceView.getHeight());
|
||||
|
||||
glSurfaceView.onResume();
|
||||
glSurfaceView.requestRender();
|
||||
|
||||
renderer.setOnSurfaceTextureReadyListener(surfaceTexture -> {
|
||||
mainHandler.post(() -> {
|
||||
Surface surface = new Surface(surfaceTexture);
|
||||
|
||||
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(url));
|
||||
exoPlayer.setMediaItem(mediaItem);
|
||||
exoPlayer.setVideoSurface(surface);
|
||||
exoPlayer.prepare();
|
||||
exoPlayer.play();
|
||||
Log.d("@@@", "ExoPlayer state after play: " + exoPlayer.getPlaybackState());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void clearPrevious() {
|
||||
// if (isDestroyed) return;
|
||||
// 确保在主线程中执行
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(() -> {
|
||||
// if (!isDestroyed) {
|
||||
clearPrevious();
|
||||
// }
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 停止并清理 ExoPlayer
|
||||
if (exoPlayer != null) {
|
||||
try {
|
||||
exoPlayer.stop(); // 这里可能会在错误线程中调用
|
||||
exoPlayer.clearVideoSurface();
|
||||
} catch (Exception e) {
|
||||
Logger.e("Error stopping ExoPlayer: " + e.getMessage());
|
||||
// 如果在错误线程中,切换到主线程重试
|
||||
mainHandler.post(() -> {
|
||||
try {
|
||||
if (exoPlayer != null) {
|
||||
exoPlayer.stop();
|
||||
exoPlayer.clearVideoSurface();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.e("Error stopping ExoPlayer on main thread: " + ex.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// 停止并清理 SVGA 动画
|
||||
if (svgaSurface != null && svgaSurface.getDrawable() instanceof SVGADrawable) {
|
||||
SVGADrawable drawable = (SVGADrawable) svgaSurface.getDrawable();
|
||||
if (drawable != null) {
|
||||
drawable.stop();
|
||||
// 清理 SVGADrawable 中的资源
|
||||
svgaSurface.clearAnimation();
|
||||
svgaSurface.setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏所有视图
|
||||
if (playerView != null) playerView.setVisibility(View.GONE);
|
||||
if (svgaSurface != null) svgaSurface.setVisibility(View.GONE);
|
||||
mBinding.playView.setVisibility(View.GONE);
|
||||
|
||||
// 停止播放器
|
||||
if (mBinding.playView != null) {
|
||||
mBinding.playView.stopPlay();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error in clearPrevious: " + e.getMessage());
|
||||
}
|
||||
|
||||
// if (svgaSurface.getDrawable() instanceof SVGADrawable) {
|
||||
// ((SVGADrawable) svgaSurface.getDrawable()).stop();
|
||||
// }
|
||||
|
||||
// if (playerView != null) playerView.setVisibility(View.GONE);
|
||||
// if (svgaSurface != null) svgaSurface.setVisibility(View.GONE);
|
||||
// if (glSurfaceView != null) glSurfaceView.setVisibility(View.GONE);
|
||||
// mBinding.playView.setVisibility(View.GONE);
|
||||
}
|
||||
// 简单的 LRU Cache 实现
|
||||
private static class LruCache<K, V> extends LinkedHashMap<K, V> {
|
||||
private final int maxSize;
|
||||
|
||||
public LruCache(int maxSize) {
|
||||
super(16, 0.75f, true);
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() > maxSize;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 释放所有资源
|
||||
*/
|
||||
private void releaseResources() {
|
||||
LogUtils.d(TAG, "Releasing all resources");
|
||||
|
||||
// if (isDestroyed) return;
|
||||
|
||||
try {
|
||||
// 清理 SVGA 资源
|
||||
if (svgaSurface != null && svgaSurface.getDrawable() instanceof SVGADrawable) {
|
||||
SVGADrawable drawable = (SVGADrawable) svgaSurface.getDrawable();
|
||||
if (drawable != null) {
|
||||
try {
|
||||
drawable.stop();
|
||||
svgaSurface.clearAnimation();
|
||||
svgaSurface.setImageDrawable(null);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error releasing SVGA resources: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 停止并清理播放器
|
||||
if (mBinding != null && mBinding.playView != null) {
|
||||
mBinding.playView.stopPlay();
|
||||
}
|
||||
|
||||
// 清理 ExoPlayer 资源
|
||||
if (exoPlayer != null) {
|
||||
try {
|
||||
exoPlayer.stop();
|
||||
exoPlayer.clearVideoSurface();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error releasing ExoPlayer resources: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error in releaseResources: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 公共释放方法,用于外部主动释放资源
|
||||
*/
|
||||
public void release() {
|
||||
Logger.d("AvatarFrameView", "Public release called");
|
||||
// if (isDestroyed) return;
|
||||
// 确保在主线程中执行
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mainHandler.post(this::release);
|
||||
return;
|
||||
}
|
||||
// isDestroyed = true;
|
||||
|
||||
try {
|
||||
// 清空播放队列
|
||||
clearQueue();
|
||||
|
||||
// 释放所有资源
|
||||
releaseResources();
|
||||
|
||||
// 释放 ExoPlayer
|
||||
if (exoPlayer != null) {
|
||||
try {
|
||||
exoPlayer.stop();
|
||||
exoPlayer.release();
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error releasing ExoPlayer: " + e.getMessage());
|
||||
}
|
||||
exoPlayer = null;
|
||||
}
|
||||
|
||||
// 清理 PlayerView
|
||||
if (playerView != null) {
|
||||
try {
|
||||
playerView.setPlayer(null);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error releasing PlayerView: " + e.getMessage());
|
||||
}
|
||||
playerView = null;
|
||||
}
|
||||
|
||||
// 清理 SVGAImageView
|
||||
if (svgaSurface != null) {
|
||||
try {
|
||||
svgaSurface.pauseAnimation();
|
||||
svgaSurface.clearAnimation();
|
||||
svgaSurface.setImageDrawable(null);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error releasing SVGAImageView: " + e.getMessage());
|
||||
}
|
||||
svgaSurface = null;
|
||||
}
|
||||
|
||||
// 清理 binding
|
||||
if (mBinding != null) {
|
||||
mBinding = null;
|
||||
}
|
||||
|
||||
// 清理队列
|
||||
playQueue.clear();
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "Error in AvatarFrameView release: " + e.getMessage());
|
||||
} finally {
|
||||
// 建议进行垃圾回收
|
||||
MemoryOptimizationUtils.forceGC();
|
||||
}
|
||||
}
|
||||
public void clearQueue() {
|
||||
// if (isDestroyed) return;
|
||||
playQueue.clear();
|
||||
isPlaying = false;
|
||||
// 清理当前正在播放的内容
|
||||
clearPrevious();
|
||||
}
|
||||
|
||||
|
||||
private static class PlayItem {
|
||||
String url;
|
||||
int type;
|
||||
|
||||
PlayItem(String url, int type) {
|
||||
this.url = url;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 关闭特效
|
||||
*/
|
||||
public void closeEffect() {
|
||||
|
||||
// 清空队列
|
||||
clearQueue();
|
||||
// 释放资源
|
||||
releaseResources();
|
||||
//清空队列
|
||||
// playQueue.clear();
|
||||
//关闭动画
|
||||
// if (mBinding.image != null && isPlaying && mBinding.image.isAnimating()) {
|
||||
// mBinding.image.setAnimation(null);
|
||||
// mBinding.image.clearAnimation();
|
||||
// mBinding.image.stopAnimation();
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,762 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.CountDownTimer;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.opensource.svgaplayer.SVGAImageView;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.base.RoomRollModel;
|
||||
import com.xscm.moduleutil.bean.FaceBean;
|
||||
import com.xscm.moduleutil.bean.UserOnlineStatusBean;
|
||||
import com.xscm.moduleutil.bean.room.ClosePhone;
|
||||
import com.xscm.moduleutil.bean.room.RoomClearCardiacAllModel;
|
||||
import com.xscm.moduleutil.bean.room.RoomClearCardiacModel;
|
||||
import com.xscm.moduleutil.bean.room.RoomClosePitModel;
|
||||
import com.xscm.moduleutil.bean.room.RoomCountDownModel;
|
||||
import com.xscm.moduleutil.bean.room.RoomDownWheatModel;
|
||||
import com.xscm.moduleutil.bean.room.RoomGiveGiftModel;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
import com.xscm.moduleutil.bean.room.RoomWheatModel;
|
||||
import com.xscm.moduleutil.event.RoomBanWheatEvent;
|
||||
import com.xscm.moduleutil.event.RoomBeckoningEvent;
|
||||
import com.xscm.moduleutil.event.RoomCardiacValueChangedEvent;
|
||||
import com.xscm.moduleutil.event.RoomFaceEvent;
|
||||
import com.xscm.moduleutil.interfaces.IBaseWheat;
|
||||
import com.xscm.moduleutil.interfaces.SoundLevelUpdateListener;
|
||||
import com.xscm.moduleutil.rtc.AgoraManager;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
import com.xscm.moduleutil.utils.logger.Logger;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
public abstract class BaseWheatView extends ConstraintLayout implements IBaseWheat {
|
||||
public ImageView mRiv;
|
||||
public ImageView mIvGift;
|
||||
public WheatCharmView mCharmView;
|
||||
public TextView mTvName;
|
||||
public ImageView mIvSex;
|
||||
public AvatarFrameView mIvFrame;
|
||||
public SVGAImageView mIvRipple;
|
||||
public ExpressionImgView mIvFace;
|
||||
public ImageView mIvShutup;
|
||||
public TextView tvTime;
|
||||
public TextView mTvNo;
|
||||
|
||||
public TextView tv_time_pk;
|
||||
|
||||
public RoomPitBean pitBean;//麦位数据
|
||||
public String roomId;//房间id
|
||||
|
||||
CountDownTimer mCountDownTimer;
|
||||
|
||||
public static final String WHEAT_BOSS = "8";//老板位
|
||||
|
||||
public static final String WHEAT_HOST = "9";//主持位
|
||||
|
||||
public float oX;
|
||||
public float oY;
|
||||
|
||||
boolean closePhone = false;//自己麦位关闭话筒,用于判断声纹显示
|
||||
|
||||
public String pitNumber;
|
||||
public int pitImageVId;
|
||||
|
||||
public ImageView iv_on_line;
|
||||
private boolean showGiftAnim = true;//显示麦位动画
|
||||
private ImageView iv_tag_type;
|
||||
|
||||
private TextView tv_zhul;
|
||||
|
||||
public BaseWheatView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public BaseWheatView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BaseWheatView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
inflate(context, getLayoutId(), this);
|
||||
mRiv = findViewById(R.id.riv);
|
||||
mIvGift = findViewById(R.id.iv_gift);
|
||||
mCharmView = findViewById(R.id.charm_view);
|
||||
mTvName = findViewById(R.id.tv_name);
|
||||
mIvSex = findViewById(R.id.iv_sex);
|
||||
mIvFrame = findViewById(R.id.iv_frame);
|
||||
mIvRipple = findViewById(R.id.iv_ripple);
|
||||
mIvFace = findViewById(R.id.iv_face);
|
||||
mIvShutup = findViewById(R.id.iv_shutup);
|
||||
tvTime = findViewById(R.id.tv_time);
|
||||
tv_time_pk = findViewById(R.id.tv_time_pk);
|
||||
mTvNo = findViewById(R.id.tv_no);
|
||||
iv_on_line = findViewById(R.id.iv_online);
|
||||
iv_tag_type = findViewById(R.id.iv_tag_type);
|
||||
tv_zhul=findViewById(R.id.tv_zhul);
|
||||
setClipChildren(false);
|
||||
setClipToPadding(false);
|
||||
oX = mIvGift.getX();
|
||||
oY = mIvGift.getY();
|
||||
initPit(context, attrs);
|
||||
}
|
||||
|
||||
protected abstract void initPit(Context context, AttributeSet attrs);
|
||||
|
||||
protected abstract int getLayoutId();
|
||||
|
||||
protected abstract void setPitData(RoomPitBean bean);
|
||||
|
||||
protected float getTzbl() {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
private @DrawableRes int mOriginImage = 0;
|
||||
private @DrawableRes int mOriginNoImage = 0;
|
||||
|
||||
public @DrawableRes int getOriginImage() {
|
||||
return mOriginImage;
|
||||
}
|
||||
|
||||
public @DrawableRes int getOriginNoImage() {
|
||||
return mOriginNoImage;
|
||||
}
|
||||
|
||||
public void setImageResource(@DrawableRes int image, @DrawableRes int noImage, CharSequence no) {
|
||||
mOriginImage = image;
|
||||
mOriginNoImage = noImage;
|
||||
mRiv.setImageResource(mOriginImage);
|
||||
if (mTvNo != null) {
|
||||
mTvNo.setText(no);
|
||||
mTvNo.setBackgroundResource(mOriginNoImage);
|
||||
mTvNo.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置麦位数据
|
||||
*
|
||||
* @param bean
|
||||
*/
|
||||
@Override
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void setData(RoomPitBean bean) {
|
||||
if (!pitNumber.equals(bean.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
this.pitBean = bean;
|
||||
this.roomId = bean.getRoom_id();
|
||||
countDownTime(bean.getCount_down());
|
||||
|
||||
setCardiac(pitBean.getCharm(), getTzbl());
|
||||
setPitData(bean);
|
||||
// if (bean.getIs_online() == 0 &&bean.getUser_id() != null && !bean.getUser_id().equals("0") && !bean.getUser_id().isEmpty()) {
|
||||
// iv_on_line.setVisibility(VISIBLE);
|
||||
// } else {
|
||||
// iv_on_line.setVisibility(GONE);
|
||||
// }
|
||||
// if (isOn() && bean.getBall_state() == 1) {
|
||||
// gameImgView.startGame();
|
||||
// } else {
|
||||
// gameImgView.overGame();
|
||||
// }
|
||||
// String userOnlineStatusBean = SpUtil.getUserOnline();
|
||||
// if (userOnlineStatusBean!= null){
|
||||
//// if (userOnlineStatusBean.getUser_id().equals(bean.getUser_id()) && userOnlineStatusBean.getIs_online() == 1){
|
||||
//// iv_on_line.setVisibility(GONE);
|
||||
//// }else {
|
||||
//// iv_on_line.setVisibility(VISIBLE);
|
||||
//// }
|
||||
// }
|
||||
//心动值
|
||||
//显示心动
|
||||
if ("1".equals(pitBean.getShutup())) {
|
||||
mIvShutup.setVisibility(VISIBLE);
|
||||
} else {
|
||||
mIvShutup.setVisibility(GONE);
|
||||
}
|
||||
|
||||
//自动调节麦位波纹
|
||||
// if (!TextUtils.isEmpty(bean.getDress_picture())) {
|
||||
mIvRipple.setScaleX(1.1f);
|
||||
mIvRipple.setScaleY(1.1f);
|
||||
// } else {
|
||||
// mIvRipple.setScaleX(0.9f);
|
||||
// mIvRipple.setScaleY(0.9f);
|
||||
// }
|
||||
if (pitNumber.equals("9")) {
|
||||
iv_tag_type.setImageResource(R.mipmap.zc);
|
||||
if (mRiv.getLayoutParams() instanceof ConstraintLayout.LayoutParams) {
|
||||
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mRiv.getLayoutParams();
|
||||
params.matchConstraintPercentWidth = 0.66f; // 设置为 52%
|
||||
params.width = 0; // 必须设为 0dp(MATCH_CONSTRAINT)
|
||||
mRiv.setLayoutParams(params);
|
||||
}
|
||||
} else if (pitNumber.equals("10")) {
|
||||
iv_tag_type.setImageResource(R.mipmap.jb);
|
||||
if (mRiv.getLayoutParams() instanceof ConstraintLayout.LayoutParams) {
|
||||
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mRiv.getLayoutParams();
|
||||
params.matchConstraintPercentWidth = 0.66f; // 设置为 52%
|
||||
params.width = 0; // 必须设为 0dp(MATCH_CONSTRAINT)
|
||||
mRiv.setLayoutParams(params);
|
||||
}
|
||||
} else if (pitNumber.equals("-1")) {
|
||||
iv_tag_type.setImageResource(R.mipmap.mu_yc);
|
||||
if (mRiv.getLayoutParams() instanceof ConstraintLayout.LayoutParams) {
|
||||
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mRiv.getLayoutParams();
|
||||
params.matchConstraintPercentWidth = 0.66f; // 设置为 52%
|
||||
params.width = 0; // 必须设为 0dp(MATCH_CONSTRAINT)
|
||||
mRiv.setLayoutParams(params);
|
||||
}
|
||||
} else if (pitNumber.equals("0")) {
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
mIvShutup.setVisibility(VISIBLE);
|
||||
} else if (pitNumber.equals("888")) {
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
mIvShutup.setVisibility(GONE);
|
||||
} else {
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
}
|
||||
AgoraManager.getInstance(getContext()).addSoundLevelListener(new SoundLevelUpdateListener() {
|
||||
@Override
|
||||
public void onRemoteSoundLevelUpdate(String userId, int soundLevel) {
|
||||
if (userId.equals(pitBean.getUser_id())) {
|
||||
if (soundLevel == 0) {
|
||||
mIvRipple.post(() -> {
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(GONE);
|
||||
});
|
||||
mIvRipple.setVisibility(GONE);
|
||||
} else {
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
mIvRipple.post(() -> {
|
||||
mIvRipple.startAnimation();
|
||||
iv_on_line.setVisibility(GONE);
|
||||
// mIvRipple.setSource(getResources().getResourceName(R.raw.ripple3695), 2);
|
||||
|
||||
});
|
||||
}
|
||||
// if (pitBean.getUser_id().equals(SpUtil.getUserId()) && closePhone) {
|
||||
// mIvRipple.post(() -> {
|
||||
// mIvRipple.setVisibility(GONE);
|
||||
// });
|
||||
// }
|
||||
}
|
||||
// else {
|
||||
// mIvRipple.post(() -> {
|
||||
// mIvRipple.setVisibility(GONE);
|
||||
// });
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalSoundLevelUpdate(int volume) {
|
||||
if (volume == 0) {
|
||||
mIvRipple.post(() -> {
|
||||
mIvRipple.setVisibility(GONE);
|
||||
});
|
||||
} else {
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
mIvRipple.post(() -> {
|
||||
mIvRipple.startAnimation();
|
||||
iv_on_line.setVisibility(GONE);
|
||||
// mIvRipple.setSource(getResources().getResourceName(R.raw.ripple3695), 2);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userJoined(int userId, int elapsd) {
|
||||
if (pitBean != null && pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0")) {
|
||||
if (pitBean.getUser_id().equals(userId + "")) {
|
||||
iv_on_line.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userOffline(int userId, int reason) {
|
||||
if (pitBean != null && pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0")) {
|
||||
if (pitBean.getUser_id().equals(userId + "")) {
|
||||
iv_on_line.setVisibility(VISIBLE);
|
||||
}
|
||||
}else if (pitBean.getUser_id()==null || pitBean.getUser_id().equals("0") || pitBean.getUser_id().equals("")){
|
||||
iv_on_line.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
if (pitBean.getUser_id()==null || pitBean.getUser_id().equals("0") || pitBean.getUser_id().equals("") ){
|
||||
iv_on_line.setVisibility(GONE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 开始倒计时
|
||||
*
|
||||
* @param time
|
||||
*/
|
||||
public void countDownTime(int time) {
|
||||
try {
|
||||
if (time <= 0) {
|
||||
setTime(0);
|
||||
|
||||
releaseCountDownTimer();
|
||||
return;
|
||||
}
|
||||
releaseCountDownTimer();
|
||||
mCountDownTimer = new CountDownTimer(time * 1000L, 1000L) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
int time1 = (int) (millisUntilFinished / 1000);
|
||||
pitBean.setCount_down(time1);
|
||||
setTime(time1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
setTime(0);
|
||||
}
|
||||
};
|
||||
mCountDownTimer.start();
|
||||
} catch (Exception e) {
|
||||
Logger.e("countDownTime", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTime(int time) {
|
||||
if (time == 0) {
|
||||
tvTime.setText("");
|
||||
tvTime.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
tvTime.setText(String.format("%s'%s", time / 60, time % 60));
|
||||
tvTime.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
// @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
||||
public void setOnlineStatus(UserOnlineStatusBean isOnline) {
|
||||
// iv_on_line.setVisibility(isOnline ? GONE : VISIBLE);
|
||||
|
||||
if (pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0") && !pitBean.getUser_id().isEmpty()) {
|
||||
//// for (UserOnlineStatusBean userOnlineStatus : userOnlineStatusBean) {
|
||||
if (pitBean.getUser_id().equals(isOnline.getUser_id())) {
|
||||
if (isOnline.getIs_online() == 1) {
|
||||
iv_on_line.setVisibility(GONE);
|
||||
} else {
|
||||
iv_on_line.setVisibility(VISIBLE);
|
||||
}
|
||||
//// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
showGiftAnim = true;
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
showGiftAnim = false;
|
||||
releaseCountDownTimer();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放倒计时
|
||||
*/
|
||||
private void releaseCountDownTimer() {
|
||||
if (mCountDownTimer != null) {
|
||||
mCountDownTimer.cancel();
|
||||
mCountDownTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 麦位是否有人
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isOn() {
|
||||
return pitBean != null && !TextUtils.isEmpty(pitBean.getUser_id()) && !"0".equals(pitBean.getUser_id());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 显示麦位礼物动画
|
||||
*
|
||||
* @param listBean
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void showGift(RoomGiveGiftModel.GiftListBean listBean) {
|
||||
if (!showGiftAnim) {
|
||||
mIvGift.setVisibility(GONE);
|
||||
return;
|
||||
}
|
||||
if (listBean.getUser_id() == null || !listBean.getUser_id().equals(pitBean.getUser_id())) {
|
||||
return;
|
||||
}
|
||||
// ImageUtils.loadImageView(listBean.getPicture(), mIvGift);
|
||||
WheatGiftAnim.addGift(mIvGift, listBean.getPicture());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置心动值
|
||||
*
|
||||
* @param rough_number
|
||||
*/
|
||||
@Override
|
||||
public void setCardiac(String rough_number, float bl) {
|
||||
if (mCharmView != null) {
|
||||
pitBean.setCharm((rough_number != null && !rough_number.isEmpty()) ? rough_number : "0");
|
||||
if (pitBean.getUser_id() == null || pitBean.getUser_id().equals("0") || pitBean.getUser_id().equals("")) {
|
||||
// mCharmView.setVisibility(GONE);
|
||||
mCharmView.setSex(pitBean.getSex(), pitBean.getUser_id(), pitBean.getCharm(), false);
|
||||
} else {
|
||||
mCharmView.setVisibility(VISIBLE);
|
||||
mCharmView.setSex(pitBean.getSex(), pitBean.getUser_id(), pitBean.getCharm(), false);
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// } else {
|
||||
// try {
|
||||
// String d = pitBean.getXin_dong();
|
||||
// long xd = Long.parseLong(d);
|
||||
// if (xd <= 0) {
|
||||
// mCharmView.setSex(pitBean.getSex(), pitBean.getUser_id(), d, false);
|
||||
// } else {
|
||||
// float xxd = xd * bl;
|
||||
// mCharmView.setSex(pitBean.getSex(), pitBean.getUser_id(), String.format(Locale.CHINESE, "%sx%.2f=%.2f", d, bl, xxd), true);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// mCharmView.setSex(pitBean.getSex(), pitBean.getUser_id(), pitBean.getXin_dong(), false);
|
||||
// }
|
||||
// }
|
||||
EventBus.getDefault().post(new RoomCardiacValueChangedEvent(pitNumber, pitBean.getCharm()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空心动值
|
||||
*/
|
||||
@Override
|
||||
public void clearCardiac() {
|
||||
if (mCharmView != null) {
|
||||
pitBean.setCharm("0");
|
||||
mCharmView.setSex(pitBean.getSex(), pitBean.getUser_id(), pitBean.getCharm(), true);
|
||||
EventBus.getDefault().post(new RoomCardiacValueChangedEvent(pitNumber, pitBean.getCharm()));
|
||||
}
|
||||
}
|
||||
|
||||
public int getCardiac() {
|
||||
if (pitBean == null) {
|
||||
return 0;
|
||||
} else {
|
||||
try {
|
||||
return Integer.parseInt(pitBean.getCharm());
|
||||
} catch (Throwable e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
if (pitBean != null && pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0")) {
|
||||
return pitBean.getUser_id();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Object obj) {
|
||||
EventBus.getDefault().register(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unRegister(Object obj) {
|
||||
AgoraManager.getInstance(getContext()).removeSoundLevelListener(this);
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 心动值显示开关
|
||||
*
|
||||
* @param roomBeckoningEvent
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomBeckoningEvent roomBeckoningEvent) {
|
||||
if (roomId.equals(roomBeckoningEvent.getRoomId())) {
|
||||
mCharmView.setVisibility(roomBeckoningEvent.isOpen() ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 心动值变化
|
||||
*
|
||||
* @param cardiacListBean
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomGiveGiftModel.CardiacListBean cardiacListBean) {
|
||||
if (!roomId.equals(cardiacListBean.getRoom_id())) {
|
||||
return;
|
||||
}
|
||||
if (this.pitNumber.equals(cardiacListBean.getPit_number())) {
|
||||
this.setCardiac(cardiacListBean.getXin_dong(), getTzbl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空单个麦位心动值
|
||||
*
|
||||
* @param roomClearCardiacModel
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomClearCardiacModel roomClearCardiacModel) {
|
||||
if (!roomId.equals(roomClearCardiacModel.getRoom_id())) {
|
||||
return;
|
||||
}
|
||||
if (this.pitNumber.equals(roomClearCardiacModel.getPit_number())) {
|
||||
clearCardiac();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清空所有心动值
|
||||
*
|
||||
* @param roomClearCardiacAllModel
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomClearCardiacAllModel roomClearCardiacAllModel) {
|
||||
if (!roomId.equals(roomClearCardiacAllModel.getRoom_id())) {
|
||||
return;
|
||||
}
|
||||
clearCardiac();
|
||||
}
|
||||
|
||||
/**
|
||||
* 上麦
|
||||
*
|
||||
* @param roomWheatModel
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomWheatModel roomWheatModel) {
|
||||
if (!roomId.equals(roomWheatModel.getRoom_id()) || !pitNumber.equals(roomWheatModel.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
pitBean.setNickname(roomWheatModel.getNickname());
|
||||
pitBean.setHead_picture(roomWheatModel.getHead_picture());
|
||||
pitBean.setBanned(roomWheatModel.getBanned());
|
||||
pitBean.setUser_id(roomWheatModel.getUser_id());
|
||||
pitBean.setDress_picture(roomWheatModel.getDress_picture());
|
||||
pitBean.setSex(roomWheatModel.getSex());
|
||||
pitBean.setBall_state(roomWheatModel.getBall_state());
|
||||
pitBean.setPit_number(roomWheatModel.getPit_number());
|
||||
pitBean.setCharm(roomWheatModel.getXin_dong());
|
||||
setData(pitBean);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下麦
|
||||
*
|
||||
* @param roomDownWheatModel
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomDownWheatModel roomDownWheatModel) {
|
||||
if (!roomId.equals(roomDownWheatModel.getRoom_id()) || !pitNumber.equals(roomDownWheatModel.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
closePhone = SpUtil.getUserId() == roomDownWheatModel.getUser_id();
|
||||
pitBean.setUser_id("0");
|
||||
pitBean.setPit_number(roomDownWheatModel.getPit_number());
|
||||
setData(pitBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒计时
|
||||
*
|
||||
* @param roomCountDownModel
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomCountDownModel roomCountDownModel) {
|
||||
if (!roomId.equals(roomCountDownModel.getRoom_id()) || !pitNumber.equals(roomCountDownModel.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
pitBean.setCount_down(roomCountDownModel.getSeconds());
|
||||
countDownTime(roomCountDownModel.getSeconds());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 禁麦
|
||||
*
|
||||
* @param roomBanWheatEvent
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomBanWheatEvent roomBanWheatEvent) {
|
||||
if (!roomId.equals(roomBanWheatEvent.getRoomId()) || !pitNumber.equals(roomBanWheatEvent.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
pitBean.setShutup(roomBanWheatEvent.isBanWheat() ? "1" : "2");
|
||||
setData(pitBean);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁麦
|
||||
*
|
||||
* @param roomClosePitModel
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomClosePitModel roomClosePitModel) {
|
||||
if (!roomId.equals(roomClosePitModel.getRoom_id()) || !pitNumber.equals(roomClosePitModel.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
pitBean.setState(roomClosePitModel.getAction());
|
||||
//麦位上锁
|
||||
setData(pitBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户关闭麦克风
|
||||
*
|
||||
* @param closePhone
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void subscribeMessages(ClosePhone closePhone) {
|
||||
if (pitBean.getUser_id().equals(SpUtil.getUserId())) {
|
||||
this.closePhone = closePhone.isClosePhone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 麦位是否被锁
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isLocked() {
|
||||
return "1".equals(pitBean.getIs_lock());
|
||||
}
|
||||
|
||||
public boolean isMute() {
|
||||
if (pitBean != null) {
|
||||
if (pitBean.getUser_id() != null && !pitBean.getUser_id().isEmpty() && "1".equals(pitBean.getIs_mute())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表情
|
||||
*
|
||||
* @param roomFaceEvent
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomFaceEvent roomFaceEvent) {
|
||||
if (!roomId.equals(roomFaceEvent.getRoom_id()) || !pitNumber.equals(roomFaceEvent.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIvFace.addData(new FaceBean(roomFaceEvent.getSpecial(), roomFaceEvent.getTime(), 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 抽签
|
||||
*
|
||||
* @param roomRollModel
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Override
|
||||
public void subscribeMessages(RoomRollModel roomRollModel) {
|
||||
if (!roomId.equals(roomRollModel.getRoom_id()) || !pitNumber.equals(roomRollModel.getPit_number())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mIvFace.addData(new FaceBean(roomRollModel.getNumber(), 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 球球大作战开球
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
|
||||
/**
|
||||
* 是否主持
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isHost() {
|
||||
return WHEAT_HOST.equals(pitNumber);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onRemoteSoundLevelUpdate(String userId, int volume) {
|
||||
// if (userId.equals(pitBean.getUser_id())) {
|
||||
// if (volume == 0) {
|
||||
// mIvRipple.post(() -> {
|
||||
// mIvRipple.setVisibility(GONE);
|
||||
// });
|
||||
// } else {
|
||||
// mIvRipple.post(() -> {
|
||||
// if (!mIvRipple.isAnimating()) {
|
||||
// mIvRipple.startAnimation();
|
||||
// }
|
||||
// mIvRipple.setVisibility(VISIBLE);
|
||||
// });
|
||||
// }
|
||||
// if (pitBean.getUser_id().equals(SpUtil.getUserId()) && closePhone) {
|
||||
// mIvRipple.post(() -> {
|
||||
// mIvRipple.setVisibility(GONE);
|
||||
// });
|
||||
// }
|
||||
// } else if (userId.equals("0")) {
|
||||
// mIvRipple.post(() -> {
|
||||
// mIvRipple.setVisibility(GONE);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
|
||||
/**
|
||||
* ProjectName: isolated-island
|
||||
* Package: com.qpyy.libcommon.widget
|
||||
* Description: 靓号View(有靓号一定有id特效(靓字+id),有id特效不一是靓号(只显示id))
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/3/16
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/3/16 9:59
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class BeautifulNameView extends LinearLayout {
|
||||
private ImageView ivIcon;
|
||||
private ScanTextView stvBeautiful;
|
||||
private int textColor;
|
||||
private int lightColor;
|
||||
private float textSize;
|
||||
private int speed;
|
||||
|
||||
public BeautifulNameView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public BeautifulNameView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BeautifulNameView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
private void init(@Nullable AttributeSet attrs) {
|
||||
LayoutInflater.from(getContext()).inflate(R.layout.view_beautiful_name, this, true);
|
||||
ivIcon = findViewById(R.id.iv_liang);
|
||||
stvBeautiful = findViewById(R.id.stv_beautiful_value);
|
||||
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.BeautifulNameView);
|
||||
textColor = typedArray.getInt(R.styleable.BeautifulNameView_textColorc, Color.WHITE);
|
||||
lightColor = typedArray.getInt(R.styleable.BeautifulNameView_lightColor, Color.WHITE);
|
||||
textSize = typedArray.getDimension(R.styleable.BeautifulNameView_textSizec, 12);
|
||||
speed = typedArray.getInt(R.styleable.BeautifulNameView_speed, 50);
|
||||
typedArray.recycle();
|
||||
stvBeautiful.setTextColor(textColor);
|
||||
stvBeautiful.setLightColor(lightColor);
|
||||
stvBeautiful.setTextSize(textSize);
|
||||
stvBeautiful.setSpeed(speed);
|
||||
ImageUtils.loadRes(R.mipmap.me_icon_liang, ivIcon);
|
||||
}
|
||||
|
||||
public void setImg(String url) {
|
||||
if (!TextUtils.isEmpty(url)) {
|
||||
ImageUtils.loadIcon(url, ivIcon);
|
||||
}
|
||||
}
|
||||
|
||||
public void setImg(int resId) {
|
||||
ImageUtils.loadRes(resId, ivIcon);
|
||||
}
|
||||
|
||||
public void setImgVisible(boolean visible) {
|
||||
ivIcon.setVisibility(visible ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void setTextColor(String color) {
|
||||
if (TextUtils.isEmpty(color) || !color.startsWith("#")) {
|
||||
return;
|
||||
}
|
||||
stvBeautiful.setTextColor(Color.parseColor(color));
|
||||
}
|
||||
|
||||
public void setTextColor(int color) {
|
||||
stvBeautiful.setTextColor(color);
|
||||
}
|
||||
|
||||
public void setTextSize(float textSize) {
|
||||
stvBeautiful.setTextSize(textSize);
|
||||
}
|
||||
|
||||
public void setText(String value) {
|
||||
if (!TextUtils.isEmpty(value)) {
|
||||
stvBeautiful.setText(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 只有服务端返回颜色(id_color字段)才播动效
|
||||
* @param play
|
||||
*/
|
||||
public void setPlay(boolean play) {
|
||||
stvBeautiful.setScan(play);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.xscm.moduleutil.widget
|
||||
|
||||
|
||||
import android.graphics.SurfaceTexture
|
||||
import android.opengl.GLES20
|
||||
import android.opengl.Matrix
|
||||
|
||||
class ChannelSplitRenderer {
|
||||
|
||||
private val vertexShaderCode = """
|
||||
attribute vec4 aPosition;
|
||||
attribute vec2 aTexCoord;
|
||||
varying vec2 vTexCoord;
|
||||
void main() {
|
||||
gl_Position = aPosition;
|
||||
vTexCoord = aTexCoord;
|
||||
}
|
||||
"""
|
||||
|
||||
private val fragmentShaderCode = """
|
||||
precision mediump float;
|
||||
uniform sampler2D uTexture;
|
||||
varying vec2 vTexCoord;
|
||||
void main() {
|
||||
// 只使用左半部分作为最终颜色
|
||||
vec2 leftCoord = vec2(vTexCoord.x * 0.5, vTexCoord.y);
|
||||
vec4 color = texture2D(uTexture, leftCoord);
|
||||
|
||||
// 设置 alpha 为 1.0 表示完全不透明,或根据需求设为 0.0 表示全透明
|
||||
gl_FragColor = vec4(color.rgb, 0.0); // 左通道颜色 + 不透明
|
||||
}"""
|
||||
|
||||
private var program = 0
|
||||
private var positionHandle = 0
|
||||
private var texCoordHandle = 0
|
||||
private var textureHandle = 0
|
||||
private val projectionMatrix = FloatArray(16)
|
||||
private val modelMatrix = FloatArray(16)
|
||||
|
||||
fun onSurfaceCreated(surface: SurfaceTexture, width: Int, height: Int) {
|
||||
// 初始化着色器
|
||||
val vertexShader = ShaderUtils.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
|
||||
val fragmentShader = ShaderUtils.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
|
||||
|
||||
program = GLES20.glCreateProgram().also {
|
||||
GLES20.glAttachShader(it, vertexShader)
|
||||
GLES20.glAttachShader(it, fragmentShader)
|
||||
GLES20.glLinkProgram(it)
|
||||
}
|
||||
|
||||
positionHandle = GLES20.glGetAttribLocation(program, "aPosition")
|
||||
texCoordHandle = GLES20.glGetAttribLocation(program, "aTexCoord")
|
||||
textureHandle = GLES20.glGetUniformLocation(program, "uTexture")
|
||||
|
||||
// 初始化矩阵
|
||||
Matrix.setIdentityM(projectionMatrix, 0)
|
||||
Matrix.setIdentityM(modelMatrix, 0)
|
||||
|
||||
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
|
||||
GLES20.glEnable(GLES20.GL_BLEND)
|
||||
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)
|
||||
}
|
||||
|
||||
fun onSurfaceChanged(width: Int, height: Int) {
|
||||
GLES20.glViewport(0, 0, width, height)
|
||||
}
|
||||
|
||||
fun onDrawFrame(textureId: Int) {
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||
GLES20.glUseProgram(program)
|
||||
|
||||
// 定义顶点坐标(全屏)
|
||||
val vertices = floatArrayOf(
|
||||
-1.0f, -1.0f, 0.0f,
|
||||
1.0f, -1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f,
|
||||
1.0f, 1.0f, 0.0f
|
||||
)
|
||||
|
||||
// 修改纹理坐标,只映射左半部分视频内容到左侧屏幕
|
||||
val texCoords = floatArrayOf(
|
||||
0.0f, 1.0f, // 左下角
|
||||
0.5f, 1.0f, // 右下角(对应视频中间)
|
||||
0.0f, 0.0f, // 左上角
|
||||
0.5f, 0.0f // 右上角
|
||||
)
|
||||
|
||||
val vertexBuffer = ShaderUtils.createFloatBuffer(vertices)
|
||||
val texBuffer = ShaderUtils.createFloatBuffer(texCoords)
|
||||
|
||||
GLES20.glEnableVertexAttribArray(positionHandle)
|
||||
GLES20.glVertexAttribPointer(
|
||||
positionHandle, 3,
|
||||
GLES20.GL_FLOAT, false,
|
||||
0, vertexBuffer
|
||||
)
|
||||
|
||||
GLES20.glEnableVertexAttribArray(texCoordHandle)
|
||||
GLES20.glVertexAttribPointer(
|
||||
texCoordHandle, 2,
|
||||
GLES20.GL_FLOAT, false,
|
||||
0, texBuffer
|
||||
)
|
||||
|
||||
// 绑定纹理
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
|
||||
GLES20.glUniform1i(textureHandle, 0)
|
||||
|
||||
// 绘制
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
|
||||
|
||||
// 清理
|
||||
GLES20.glDisableVertexAttribArray(positionHandle)
|
||||
GLES20.glDisableVertexAttribArray(texCoordHandle)
|
||||
}
|
||||
|
||||
|
||||
fun release() {
|
||||
GLES20.glDeleteProgram(program)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.opengl.Matrix;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)
|
||||
public class ChannelSplitRenderer1 implements GLSurfaceView.Renderer {
|
||||
|
||||
private static final String VERTEX_SHADER =
|
||||
"uniform mat4 uMVPMatrix;\n" +
|
||||
"uniform mat4 uSTMatrix;\n" +
|
||||
"attribute vec4 aPosition;\n" +
|
||||
"attribute vec4 aTextureCoord;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_Position = uMVPMatrix * aPosition;\n" +
|
||||
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
|
||||
"}\n";
|
||||
|
||||
private static final String FRAGMENT_SHADER =
|
||||
"#extension GL_OES_EGL_image_external : require\n" +
|
||||
"precision mediump float;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"uniform samplerExternalOES sTexture;\n" +
|
||||
"void main() {\n" +
|
||||
" vec2 uv = vTextureCoord;\n" +
|
||||
" // 左边部分的 UV 坐标范围 (0.0, 0.1) 到 (0.6, 0.9)\n" +
|
||||
" if (uv.x >= 0.0 && uv.x <= 0.6 && uv.y >= 0.1 && uv.y <= 0.9) {\n" +
|
||||
" // 居中显示\n" +
|
||||
" vec2 newUV = vec2(uv.x * (1.0 / 0.6), uv.y); // 水平方向拉伸\n" +
|
||||
" gl_FragColor = texture2D(sTexture, newUV);\n" +
|
||||
" } else {\n" +
|
||||
" gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); // 设置为完全透明\n" +
|
||||
" }\n" +
|
||||
"}\n";
|
||||
|
||||
|
||||
private int mProgram;
|
||||
private int maPositionHandle;
|
||||
private int maTextureHandle;
|
||||
private int muMVPMatrixHandle;
|
||||
private int muSTMatrixHandle;
|
||||
|
||||
private float[] mMVPMatrix = new float[16];
|
||||
private float[] mSTMatrix = new float[16];
|
||||
|
||||
private SurfaceTexture surfaceTexture;
|
||||
private int externalTextureId = -1;
|
||||
|
||||
private final float[] mTriangleVerticesData = {
|
||||
// X, Y, Z, U, V
|
||||
-1.0f, -1.0f, 0, 0.f, 0.f,
|
||||
1.0f, -1.0f, 0, 1.f, 0.f,
|
||||
-1.0f, 1.0f, 0, 0.f, 1.f,
|
||||
1.0f, 1.0f, 0, 1.f, 1.f,
|
||||
};
|
||||
|
||||
private FloatBuffer mTriangleVertices;
|
||||
|
||||
public ChannelSplitRenderer1() {
|
||||
mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * 4)
|
||||
.order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
mTriangleVertices.put(mTriangleVerticesData).position(0);
|
||||
Matrix.setIdentityM(mSTMatrix, 0);
|
||||
}
|
||||
|
||||
public interface OnSurfaceTextureReadyListener {
|
||||
void onSurfaceTextureReady(SurfaceTexture surfaceTexture);
|
||||
}
|
||||
private OnSurfaceTextureReadyListener surfaceTextureReadyListener;
|
||||
|
||||
public void setOnSurfaceTextureReadyListener(OnSurfaceTextureReadyListener listener) {
|
||||
this.surfaceTextureReadyListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, javax.microedition.khronos.egl.EGLConfig config) {
|
||||
Log.d("@@@@", "onDrawFrame called");
|
||||
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
|
||||
GLES20.glDisable(GLES20.GL_STENCIL_TEST);
|
||||
GLES20.glDisable(GLES20.GL_BLEND);
|
||||
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 设置透明背景
|
||||
|
||||
int[] textures = new int[1];
|
||||
GLES20.glGenTextures(1, textures, 0);
|
||||
externalTextureId = textures[0];
|
||||
|
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, externalTextureId);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
||||
|
||||
surfaceTexture = new SurfaceTexture(externalTextureId);
|
||||
|
||||
mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
||||
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
|
||||
maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
|
||||
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
|
||||
muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
|
||||
|
||||
if (surfaceTextureReadyListener != null) {
|
||||
surfaceTextureReadyListener.onSurfaceTextureReady(surfaceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||
GLES20.glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
private long lastTimestamp = -1;
|
||||
@Override
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (surfaceTexture != null) {
|
||||
surfaceTexture.updateTexImage();
|
||||
long timestamp = surfaceTexture.getTimestamp();
|
||||
|
||||
if (timestamp != lastTimestamp) {
|
||||
Log.d("@@@", "New frame received, timestamp: " + timestamp);
|
||||
lastTimestamp = timestamp;
|
||||
}
|
||||
|
||||
surfaceTexture.getTransformMatrix(mSTMatrix);
|
||||
} else {
|
||||
Log.e("@@@", "SurfaceTexture is null in onDrawFrame");
|
||||
}
|
||||
|
||||
GLES20.glUseProgram(mProgram);
|
||||
|
||||
Matrix.setIdentityM(mMVPMatrix, 0);
|
||||
|
||||
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
|
||||
GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
|
||||
|
||||
mTriangleVertices.position(0);
|
||||
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 20, mTriangleVertices);
|
||||
GLES20.glEnableVertexAttribArray(maPositionHandle);
|
||||
|
||||
mTriangleVertices.position(3);
|
||||
GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, 20, mTriangleVertices);
|
||||
GLES20.glEnableVertexAttribArray(maTextureHandle);
|
||||
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, externalTextureId);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
public SurfaceTexture getSurfaceTexture() {
|
||||
return surfaceTexture;
|
||||
}
|
||||
|
||||
private int loadShader(int shaderType, String source) {
|
||||
int shader = GLES20.glCreateShader(shaderType);
|
||||
GLES20.glShaderSource(shader, source);
|
||||
GLES20.glCompileShader(shader);
|
||||
int[] compiled = new int[1];
|
||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
|
||||
if (compiled[0] == 0) {
|
||||
return 0;
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
private int createProgram(String vertexSource, String fragmentSource) {
|
||||
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
|
||||
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
|
||||
int program = GLES20.glCreateProgram();
|
||||
GLES20.glAttachShader(program, vertexShader);
|
||||
GLES20.glAttachShader(program, pixelShader);
|
||||
GLES20.glLinkProgram(program);
|
||||
int[] linkStatus = new int[1];
|
||||
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
|
||||
if (linkStatus[0] != GLES20.GL_TRUE) {
|
||||
return 0;
|
||||
}
|
||||
return program;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
/**
|
||||
* 圆形头像
|
||||
*/
|
||||
public class CircularImage extends MaskedImage {
|
||||
|
||||
public CircularImage(Context paramContext) {
|
||||
super(paramContext);
|
||||
}
|
||||
|
||||
public CircularImage(Context paramContext, AttributeSet paramAttributeSet) {
|
||||
super(paramContext, paramAttributeSet);
|
||||
}
|
||||
|
||||
public CircularImage(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
|
||||
super(paramContext, paramAttributeSet, paramInt);
|
||||
}
|
||||
|
||||
public Bitmap createMask() {
|
||||
int i = getWidth();
|
||||
int j = getHeight();
|
||||
Bitmap.Config localConfig = Bitmap.Config.ARGB_8888;
|
||||
Bitmap localBitmap = Bitmap.createBitmap(i, j, localConfig);
|
||||
Canvas localCanvas = new Canvas(localBitmap);
|
||||
Paint localPaint = new Paint(1);
|
||||
float f1 = getWidth();
|
||||
float f2 = getHeight();
|
||||
RectF localRectF = new RectF(0.0F, 0.0F, f1, f2);
|
||||
localCanvas.drawOval(localRectF, localPaint);
|
||||
return localBitmap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
/**
|
||||
* 圆形进度条控件
|
||||
* Author: JueYes jueyes_1024@163.com
|
||||
* Time: 2019-08-07 15:38
|
||||
*/
|
||||
|
||||
public class CircularProgressView extends View {
|
||||
|
||||
private Paint mBackPaint, mProgPaint; // 绘制画笔
|
||||
private RectF mRectF; // 绘制区域
|
||||
private int[] mColorArray; // 圆环渐变色
|
||||
private int mProgress; // 圆环进度(0-100)
|
||||
|
||||
public CircularProgressView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CircularProgressView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CircularProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
@SuppressLint("Recycle")
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView);
|
||||
|
||||
// 初始化背景圆环画笔
|
||||
mBackPaint = new Paint();
|
||||
mBackPaint.setStyle(Paint.Style.STROKE); // 只描边,不填充
|
||||
mBackPaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆角
|
||||
mBackPaint.setAntiAlias(true); // 设置抗锯齿
|
||||
mBackPaint.setDither(true); // 设置抖动
|
||||
mBackPaint.setStrokeWidth(typedArray.getDimension(R.styleable.CircularProgressView_backWidth, 5));
|
||||
mBackPaint.setColor(typedArray.getColor(R.styleable.CircularProgressView_backColor, Color.LTGRAY));
|
||||
|
||||
// 初始化进度圆环画笔
|
||||
mProgPaint = new Paint();
|
||||
mProgPaint.setStyle(Paint.Style.STROKE); // 只描边,不填充
|
||||
mProgPaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆角
|
||||
mProgPaint.setAntiAlias(true); // 设置抗锯齿
|
||||
mProgPaint.setDither(true); // 设置抖动
|
||||
mProgPaint.setStrokeWidth(typedArray.getDimension(R.styleable.CircularProgressView_progWidth, 10));
|
||||
mProgPaint.setColor(typedArray.getColor(R.styleable.CircularProgressView_progColor, Color.BLUE));
|
||||
|
||||
// 初始化进度圆环渐变色
|
||||
int startColor = typedArray.getColor(R.styleable.CircularProgressView_progStartColor, -1);
|
||||
int firstColor = typedArray.getColor(R.styleable.CircularProgressView_progFirstColor, -1);
|
||||
if (startColor != -1 && firstColor != -1) mColorArray = new int[]{startColor, firstColor};
|
||||
else mColorArray = null;
|
||||
|
||||
// 初始化进度
|
||||
mProgress = typedArray.getInteger(R.styleable.CircularProgressView_progressC, 0);
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int viewWide = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
|
||||
int viewHigh = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
|
||||
int mRectLength = (int) ((viewWide > viewHigh ? viewHigh : viewWide) - (mBackPaint.getStrokeWidth() > mProgPaint.getStrokeWidth() ? mBackPaint.getStrokeWidth() : mProgPaint.getStrokeWidth()));
|
||||
int mRectL = getPaddingLeft() + (viewWide - mRectLength) / 2;
|
||||
int mRectT = getPaddingTop() + (viewHigh - mRectLength) / 2;
|
||||
mRectF = new RectF(mRectL, mRectT, mRectL + mRectLength, mRectT + mRectLength);
|
||||
|
||||
// 设置进度圆环渐变色
|
||||
if (mColorArray != null && mColorArray.length > 1)
|
||||
mProgPaint.setShader(new LinearGradient(0, 0, 0, getMeasuredWidth(), mColorArray, null, Shader.TileMode.MIRROR));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
canvas.drawArc(mRectF, 0, 360, false, mBackPaint);
|
||||
canvas.drawArc(mRectF, 275, 360 * mProgress / 1000, false, mProgPaint);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取当前进度
|
||||
*
|
||||
* @return 当前进度(0-100)
|
||||
*/
|
||||
public int getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前进度
|
||||
*
|
||||
* @param progress 当前进度(0-100)
|
||||
*/
|
||||
public void setProgress(int progress) {
|
||||
this.mProgress = progress;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 设置当前进度,并展示进度动画。如果动画时间小于等于0,则不展示动画
|
||||
// *
|
||||
// * @param progress 当前进度(0-100)
|
||||
// * @param animTime 动画时间(毫秒)
|
||||
// */
|
||||
// public void setProgress(int progress, long animTime) {
|
||||
// if (animTime <= 0) setProgress(progress);
|
||||
// else {
|
||||
// ValueAnimator animator = ValueAnimator.ofInt(mProgress, progress);
|
||||
// animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
// @Override
|
||||
// public void onAnimationUpdate(ValueAnimator animation) {
|
||||
// mProgress = (int) animation.getAnimatedValue();
|
||||
// invalidate();
|
||||
// }
|
||||
// });
|
||||
// animator.setInterpolator(new OvershootInterpolator());
|
||||
// animator.setDuration(animTime);
|
||||
// animator.start();
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 设置背景圆环宽度
|
||||
*
|
||||
* @param width 背景圆环宽度
|
||||
*/
|
||||
public void setBackWidth(int width) {
|
||||
mBackPaint.setStrokeWidth(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置背景圆环颜色
|
||||
*
|
||||
* @param color 背景圆环颜色
|
||||
*/
|
||||
public void setBackColor(@ColorRes int color) {
|
||||
mBackPaint.setColor(ContextCompat.getColor(getContext(), color));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进度圆环宽度
|
||||
*
|
||||
* @param width 进度圆环宽度
|
||||
*/
|
||||
public void setProgWidth(int width) {
|
||||
mProgPaint.setStrokeWidth(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进度圆环颜色
|
||||
*
|
||||
* @param color 景圆环颜色
|
||||
*/
|
||||
public void setProgColor(@ColorRes int color) {
|
||||
mProgPaint.setColor(ContextCompat.getColor(getContext(), color));
|
||||
mProgPaint.setShader(null);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进度圆环颜色(支持渐变色)
|
||||
*
|
||||
* @param startColor 进度圆环开始颜色
|
||||
* @param firstColor 进度圆环结束颜色
|
||||
*/
|
||||
public void setProgColor(@ColorRes int startColor, @ColorRes int firstColor) {
|
||||
mColorArray = new int[]{ContextCompat.getColor(getContext(), startColor), ContextCompat.getColor(getContext(), firstColor)};
|
||||
mProgPaint.setShader(new LinearGradient(0, 0, 0, getMeasuredWidth(), mColorArray, null, Shader.TileMode.MIRROR));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进度圆环颜色(支持渐变色)
|
||||
*
|
||||
* @param colorArray 渐变色集合
|
||||
*/
|
||||
public void setProgColor(@ColorRes int[] colorArray) {
|
||||
if (colorArray == null || colorArray.length < 2) return;
|
||||
mColorArray = new int[colorArray.length];
|
||||
for (int index = 0; index < colorArray.length; index++)
|
||||
mColorArray[index] = ContextCompat.getColor(getContext(), colorArray[index]);
|
||||
mProgPaint.setShader(new LinearGradient(0, 0, 0, getMeasuredWidth(), mColorArray, null, Shader.TileMode.MIRROR));
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
/**
|
||||
* 带删除按钮的editText
|
||||
* Created by air on 2017/1/12.
|
||||
* updated by air on 2017/3/20
|
||||
*/
|
||||
public class ClearEditText extends AppCompatEditText implements View.OnFocusChangeListener, TextWatcher {
|
||||
|
||||
//EditText右侧的删除按钮
|
||||
private Drawable mClearDrawable;
|
||||
|
||||
private boolean hasFocus;
|
||||
|
||||
private onFocusListener onFocusListener;
|
||||
|
||||
public ClearEditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ClearEditText(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.editTextStyle);
|
||||
}
|
||||
|
||||
public ClearEditText(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
//获取EditText的DrawableRight,假如没有设置我们就使用默认的图片
|
||||
mClearDrawable = getCompoundDrawables()[2];
|
||||
if (mClearDrawable == null) {
|
||||
mClearDrawable = getResources().getDrawable(R.mipmap.common_et_clear_icon);
|
||||
}
|
||||
mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(),
|
||||
mClearDrawable.getIntrinsicHeight());
|
||||
// 默认设置隐藏图标
|
||||
setClearIconVisible(false);
|
||||
// 设置焦点改变的监听
|
||||
setOnFocusChangeListener(this);
|
||||
// 设置输入框里面内容发生改变的监听
|
||||
addTextChangedListener(this);
|
||||
}
|
||||
|
||||
/* *
|
||||
* @说明:isInnerWidth, isInnerHeight为ture,触摸点在删除图标之内,则视为点击了删除图标
|
||||
* event.getX() 获取相对应自身左上角的X坐标
|
||||
* event.getY() 获取相对应自身左上角的Y坐标
|
||||
* getWidth() 获取控件的宽度
|
||||
* getHeight() 获取控件的高度
|
||||
* getTotalPaddingRight() 获取删除图标左边缘到控件右边缘的距离
|
||||
* getPaddingRight() 获取删除图标右边缘到控件右边缘的距离
|
||||
* isInnerWidth:
|
||||
* getWidth() - getTotalPaddingRight() 计算删除图标左边缘到控件左边缘的距离
|
||||
* getWidth() - getPaddingRight() 计算删除图标右边缘到控件左边缘的距离
|
||||
* isInnerHeight:
|
||||
* distance 删除图标顶部边缘到控件顶部边缘的距离
|
||||
* distance + height 删除图标底部边缘到控件顶部边缘的距离
|
||||
*/
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (getCompoundDrawables()[2] != null) {
|
||||
int x = (int) event.getX();
|
||||
int y = (int) event.getY();
|
||||
Rect rect = getCompoundDrawables()[2].getBounds();
|
||||
int height = rect.height();
|
||||
int distance = (getHeight() - height) / 2;
|
||||
boolean isInnerWidth = x > (getWidth() - getTotalPaddingRight()) && x < (getWidth() - getPaddingRight());
|
||||
boolean isInnerHeight = y > distance && y < (distance + height);
|
||||
if (isInnerWidth && isInnerHeight) {
|
||||
this.setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
public void setClearIconVisible(boolean visible) {
|
||||
Drawable right = visible ? mClearDrawable : null;
|
||||
setCompoundDrawables(getCompoundDrawables()[0],
|
||||
getCompoundDrawables()[1], right, getCompoundDrawables()[3]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当ClearEditText焦点发生变化的时候,
|
||||
* 输入长度为零,隐藏删除图标,否则,显示删除图标
|
||||
*/
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
this.hasFocus = hasFocus;
|
||||
if (hasFocus) {
|
||||
setClearIconVisible(getText().length() > 0);
|
||||
if (onFocusListener != null) {
|
||||
onFocusListener.onFocus();
|
||||
}
|
||||
} else {
|
||||
setClearIconVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int count, int after) {
|
||||
if (hasFocus) {
|
||||
setClearIconVisible(s.length() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
public interface onFocusListener {
|
||||
void onFocus();
|
||||
}
|
||||
|
||||
|
||||
public void setOnFocusListener(ClearEditText.onFocusListener onFocusListener) {
|
||||
this.onFocusListener = onFocusListener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
/**
|
||||
*@author
|
||||
*@data
|
||||
*@description: listview自定义间隔
|
||||
*/
|
||||
public class CommItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
|
||||
private static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
|
||||
|
||||
private int mSpace = 1; //间隔
|
||||
private Rect mRect = new Rect(0,0,0,0);
|
||||
private Paint mPaint = new Paint();
|
||||
|
||||
private int mOrientation;
|
||||
|
||||
private CommItemDecoration(Context context, int orientation, @ColorInt int color, int space) {
|
||||
mOrientation = orientation;
|
||||
if(space>0){
|
||||
mSpace = space;
|
||||
}
|
||||
mPaint.setColor(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
super.onDraw(c, parent, state);
|
||||
if (mOrientation == VERTICAL_LIST) {
|
||||
drawVertical(c, parent);
|
||||
} else {
|
||||
drawHorizontal(c, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawVertical(Canvas c, RecyclerView parent) {
|
||||
final int left = parent.getPaddingLeft();
|
||||
final int right = parent.getWidth() - parent.getPaddingRight();
|
||||
|
||||
final int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
.getLayoutParams();
|
||||
final int top = child.getBottom() + params.bottomMargin;
|
||||
final int bottom = top + mSpace;
|
||||
mRect.set(left,top,right,bottom);
|
||||
c.drawRect(mRect,mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawHorizontal(Canvas c, RecyclerView parent) {
|
||||
final int top = parent.getPaddingTop();
|
||||
final int bottom = parent.getHeight() - parent.getPaddingBottom();
|
||||
|
||||
final int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
.getLayoutParams();
|
||||
final int left = child.getRight() + params.rightMargin;
|
||||
final int right = left + mSpace;
|
||||
mRect.set(left, top, right, bottom);
|
||||
c.drawRect(mRect,mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
if (mOrientation == VERTICAL_LIST) {
|
||||
outRect.set(0, 0, 0,mSpace);
|
||||
} else {
|
||||
outRect.set(0, 0, mSpace, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static CommItemDecoration createVertical(Context context, @ColorInt int color, int height){
|
||||
return new CommItemDecoration(context,VERTICAL_LIST,color,height);
|
||||
}
|
||||
|
||||
public static CommItemDecoration createHorizontal(Context context, @ColorInt int color, int width){
|
||||
return new CommItemDecoration(context,HORIZONTAL_LIST,color,width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,745 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.blankj.utilcode.util.LogUtils;
|
||||
import com.xscm.moduleutil.base.CommonAppContext;
|
||||
import com.xscm.moduleutil.bean.ConfigBean;
|
||||
import com.xscm.moduleutil.interfaces.CommonCallback;
|
||||
import com.xscm.moduleutil.utils.DeviceUtils;
|
||||
import com.xscm.moduleutil.utils.LanguageUtil;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
import com.xscm.moduleutil.utils.StringUtil;
|
||||
import com.xscm.moduleutil.utils.WordUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by cxf on 2017/8/4 .
|
||||
*/
|
||||
|
||||
public class CommonAppConfig {
|
||||
public static final String PACKAGE_NAME = "com.xscm.liveShort";
|
||||
public static final String PACKAGE_NAME1 = "com.xscm.midi";
|
||||
//Http请求头 Header
|
||||
public static final Map<String, String> HEADER = new HashMap<>();
|
||||
//域名
|
||||
public static final String HOST = getHost();
|
||||
|
||||
public static final String EXTERNAL_PATH = getExternalPath();
|
||||
public static final String DOWNLOAD_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath();
|
||||
public static final String VIDEO_PATH = EXTERNAL_PATH + "/video/";
|
||||
public static final String VIDEO_RECORD_PATH = VIDEO_PATH + "/record/";
|
||||
public static final String VIDEO_RECORD_PARTS_PATH = VIDEO_RECORD_PATH + "/parts/";
|
||||
//下载视频保存路径
|
||||
public static final String VIDEO_DOWNLOAD_PATH = DOWNLOAD_PATH + "/video/";
|
||||
//下载音乐保存路径
|
||||
public static final String MUSIC_PATH = EXTERNAL_PATH + "/music/";
|
||||
|
||||
public static final String IMAGE_PATH = EXTERNAL_PATH + "/image/";
|
||||
//下载图片保存路径
|
||||
public static final String IMAGE_DOWNLOAD_PATH = DOWNLOAD_PATH + "/image/";
|
||||
//log保存路径
|
||||
public static final String LOG_PATH = EXTERNAL_PATH + "/log/";
|
||||
|
||||
public static final String GIF_PATH = EXTERNAL_PATH + "/gif/";
|
||||
public static final String WATER_MARK_PATH = EXTERNAL_PATH + "/water/";
|
||||
public static final String IM_SOUND = EXTERNAL_PATH + "/im_sound/";
|
||||
public static final String IM_IMAGE = EXTERNAL_PATH + "/im_image/";
|
||||
//腾讯IM appId
|
||||
public static final int TX_IM_APP_ID = getMetaDataInt("TxIMAppId");
|
||||
//QQ登录是否与PC端互通
|
||||
public static final boolean QQ_LOGIN_WITH_PC = false;
|
||||
//是否使用游戏
|
||||
public static final boolean GAME_ENABLE = true;
|
||||
//是否上下滑动切换直播间
|
||||
public static final boolean LIVE_ROOM_SCROLL = true;
|
||||
|
||||
private static String getExternalPath() {
|
||||
String outPath = null;
|
||||
try {
|
||||
File externalFilesDir = CommonAppContext.getInstance().getExternalFilesDir("qixing");
|
||||
if (externalFilesDir != null) {
|
||||
if (!externalFilesDir.exists()) {
|
||||
externalFilesDir.mkdirs();
|
||||
}
|
||||
outPath = externalFilesDir.getAbsolutePath();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
outPath = null;
|
||||
}
|
||||
if (TextUtils.isEmpty(outPath)) {
|
||||
outPath = CommonAppContext.getInstance().getFilesDir().getAbsolutePath();
|
||||
}
|
||||
return outPath;
|
||||
}
|
||||
|
||||
private static CommonAppConfig sInstance;
|
||||
|
||||
private CommonAppConfig() {
|
||||
|
||||
}
|
||||
|
||||
public static CommonAppConfig getInstance() {
|
||||
if (sInstance == null) {
|
||||
synchronized (CommonAppConfig.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new CommonAppConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private String mUid;
|
||||
private String mToken;
|
||||
private ConfigBean mConfig;
|
||||
private double mLng;
|
||||
private double mLat;
|
||||
private String mProvince;//省
|
||||
private String mCity;//市
|
||||
private String mDistrict;//区
|
||||
// private UserBean mUserBean;
|
||||
// private UserBean mEmptyUserBean;//未登录游客
|
||||
private String mVersion;
|
||||
private boolean mLaunched;//App是否启动了
|
||||
// private SparseArray<LevelBean> mLevelMap;
|
||||
// private SparseArray<LevelBean> mAnchorLevelMap;
|
||||
private String mGiftListJson;
|
||||
private String mGiftDaoListJson;
|
||||
private String mTxMapAppKey;//腾讯定位,地图的AppKey
|
||||
private String mTxMapAppSecret;//腾讯地图的AppSecret
|
||||
private boolean mFrontGround;
|
||||
private int mAppIconRes;
|
||||
private String mAppName;
|
||||
private Boolean mMhBeautyEnable;//是否使用美狐 true使用美狐 false 使用基础美颜
|
||||
private String mDeviceId;
|
||||
private Boolean mTeenagerType;//是否是青少年模式
|
||||
private int mTopActivityType;//最上面的Activity的类型 1直播间 2消息
|
||||
private boolean mShowLiveFloatWindow;//退出直播后是否显示直播悬浮窗
|
||||
|
||||
public String getUid() {
|
||||
if (TextUtils.isEmpty(mUid)) {
|
||||
String[] uidAndToken = SpUtil.getInstance()
|
||||
.getMultiStringValue(new String[]{SpUtil.UID, SpUtil.TOKEN});
|
||||
if (uidAndToken != null) {
|
||||
if (!TextUtils.isEmpty(uidAndToken[0]) && !TextUtils.isEmpty(uidAndToken[1])) {
|
||||
mUid = uidAndToken[0];
|
||||
mToken = uidAndToken[1];
|
||||
} else {
|
||||
mUid = Constants.NOT_LOGIN_UID;
|
||||
mToken = Constants.NOT_LOGIN_TOKEN;
|
||||
}
|
||||
} else {
|
||||
mUid = Constants.NOT_LOGIN_UID;
|
||||
mToken = Constants.NOT_LOGIN_TOKEN;
|
||||
}
|
||||
}
|
||||
return mUid;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return mToken;
|
||||
}
|
||||
|
||||
public boolean isLogin() {
|
||||
return !Constants.NOT_LOGIN_UID.equals(getUid());
|
||||
}
|
||||
|
||||
public String getCoinName() {
|
||||
ConfigBean configBean = getConfig();
|
||||
if (configBean != null) {
|
||||
return configBean.getCoinName();
|
||||
}
|
||||
return Constants.DIAMONDS;
|
||||
}
|
||||
|
||||
public String getVotesName() {
|
||||
ConfigBean configBean = getConfig();
|
||||
if (configBean != null) {
|
||||
return configBean.getVotesName();
|
||||
}
|
||||
return Constants.VOTES;
|
||||
}
|
||||
|
||||
|
||||
public String getScoreName() {
|
||||
ConfigBean configBean = getConfig();
|
||||
if (configBean != null) {
|
||||
return configBean.getScoreName();
|
||||
}
|
||||
return Constants.SCORE;
|
||||
}
|
||||
|
||||
public ConfigBean getConfig() {
|
||||
if (mConfig == null) {
|
||||
String configString = SpUtil.getInstance().getStringValue(SpUtil.CONFIG);
|
||||
if (!TextUtils.isEmpty(configString)) {
|
||||
mConfig = JSON.parseObject(configString, ConfigBean.class);
|
||||
}
|
||||
}
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
public void getConfig(CommonCallback<ConfigBean> callback) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
ConfigBean configBean = getConfig();
|
||||
if (configBean != null) {
|
||||
callback.callback(configBean);
|
||||
} else {
|
||||
// CommonHttpUtil.getConfig(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public void setConfig(ConfigBean config) {
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
public double getLng() {
|
||||
if (mLng == 0) {
|
||||
String lng = SpUtil.getInstance().getStringValue(SpUtil.LOCATION_LNG);
|
||||
if (!TextUtils.isEmpty(lng)) {
|
||||
try {
|
||||
mLng = Double.parseDouble(lng);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mLng;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
public double getLat() {
|
||||
if (mLat == 0) {
|
||||
String lat = SpUtil.getInstance().getStringValue(SpUtil.LOCATION_LAT);
|
||||
if (!TextUtils.isEmpty(lat)) {
|
||||
try {
|
||||
mLat = Double.parseDouble(lat);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mLat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 省
|
||||
*/
|
||||
public String getProvince() {
|
||||
if (TextUtils.isEmpty(mProvince)) {
|
||||
mProvince = SpUtil.getInstance().getStringValue(SpUtil.LOCATION_PROVINCE);
|
||||
}
|
||||
return mProvince == null ? "" : mProvince;
|
||||
}
|
||||
|
||||
/**
|
||||
* 市
|
||||
*/
|
||||
public String getCity() {
|
||||
if (TextUtils.isEmpty(mCity)) {
|
||||
mCity = SpUtil.getInstance().getStringValue(SpUtil.LOCATION_CITY);
|
||||
}
|
||||
return mCity == null ? "" : mCity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 区
|
||||
*/
|
||||
public String getDistrict() {
|
||||
if (TextUtils.isEmpty(mDistrict)) {
|
||||
mDistrict = SpUtil.getInstance().getStringValue(SpUtil.LOCATION_DISTRICT);
|
||||
}
|
||||
return mDistrict == null ? "" : mDistrict;
|
||||
}
|
||||
|
||||
// public void setUserBean(UserBean bean) {
|
||||
// mUserBean = bean;
|
||||
// }
|
||||
|
||||
// public UserBean getUserBean() {
|
||||
// if (mUserBean == null) {
|
||||
// String userBeanJson = SpUtil.getInstance().getStringValue(SpUtil.USER_INFO);
|
||||
// if (!TextUtils.isEmpty(userBeanJson)) {
|
||||
//// mUserBean = JSON.parseObject(userBeanJson, UserBean.class);
|
||||
// }
|
||||
// }
|
||||
// if (mUserBean == null) {
|
||||
//// mUserBean = getEmptyUserBean();
|
||||
// }
|
||||
// return mUserBean;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 设置美狐是否可用
|
||||
*/
|
||||
public void setMhBeautyEnable(boolean mhBeautyEnable) {
|
||||
mMhBeautyEnable = mhBeautyEnable;
|
||||
SpUtil.getInstance().setBooleanValue(SpUtil.MH_BEAUTY_ENABLE, mhBeautyEnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 美狐是否可用
|
||||
*/
|
||||
public boolean isMhBeautyEnable() {
|
||||
if (mMhBeautyEnable == null) {
|
||||
mMhBeautyEnable = SpUtil.getInstance().getBooleanValue(SpUtil.MH_BEAUTY_ENABLE);
|
||||
}
|
||||
return mMhBeautyEnable;
|
||||
}
|
||||
|
||||
|
||||
public void setTeenagerType(boolean teenagerType) {
|
||||
mTeenagerType = teenagerType;
|
||||
SpUtil.getInstance().setBooleanValue(SpUtil.TEENAGER, teenagerType);
|
||||
}
|
||||
|
||||
public boolean isTeenagerType() {
|
||||
if (mTeenagerType == null) {
|
||||
mTeenagerType = SpUtil.getInstance().getBooleanValue(SpUtil.TEENAGER);
|
||||
}
|
||||
return mTeenagerType;
|
||||
}
|
||||
|
||||
public void setShowLiveFloatWindow(boolean showLiveFloatWindow) {
|
||||
mShowLiveFloatWindow = showLiveFloatWindow;
|
||||
}
|
||||
|
||||
public boolean isShowLiveFloatWindow() {
|
||||
return mShowLiveFloatWindow;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置登录信息
|
||||
*/
|
||||
public void setLoginInfo(String uid, String token, boolean save) {
|
||||
LogUtils.e("登录成功", "uid------>" + uid);
|
||||
LogUtils.e("登录成功", "token------>" + token);
|
||||
mUid = uid;
|
||||
mToken = token;
|
||||
if (save) {
|
||||
SpUtil.getInstance().setBooleanValue(SpUtil.TEENAGER_SHOW, false);
|
||||
}
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put(SpUtil.UID, uid);
|
||||
map.put(SpUtil.TOKEN, token);
|
||||
SpUtil.getInstance().setMultiStringValue(map);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除登录信息
|
||||
*/
|
||||
public void clearLoginInfo() {
|
||||
mUid = null;
|
||||
mToken = null;
|
||||
SpUtil.getInstance().removeValue(
|
||||
SpUtil.UID, SpUtil.TOKEN, SpUtil.USER_INFO, SpUtil.TX_IM_USER_SIGN,
|
||||
Constants.CASH_ACCOUNT_ID, Constants.CASH_ACCOUNT, Constants.CASH_ACCOUNT_TYPE,
|
||||
SpUtil.TEENAGER, SpUtil.TEENAGER_SHOW
|
||||
);
|
||||
// CommonAppConfig.getInstance().setUserBean(getEmptyUserBean());
|
||||
mShowLiveFloatWindow = false;
|
||||
// EventBus.getDefault().post(new CloseFloatWindowEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* 未登录,游客
|
||||
*/
|
||||
// private UserBean getEmptyUserBean() {
|
||||
// if (mEmptyUserBean == null) {
|
||||
// UserBean bean = new UserBean();
|
||||
// bean.setId(Constants.NOT_LOGIN_UID);
|
||||
// bean.setUserNiceName("游客");
|
||||
// bean.setLevel(1);
|
||||
// bean.setLevelAnchor(1);
|
||||
// String defaultAvatar = CommonAppConfig.HOST + "/default.jpg";
|
||||
// bean.setAvatar(defaultAvatar);
|
||||
// bean.setAvatarThumb(defaultAvatar);
|
||||
// bean.setSignature(Constants.EMPTY_STRING);
|
||||
// bean.setCoin(Constants.EMPTY_STRING);
|
||||
// bean.setVotes(Constants.EMPTY_STRING);
|
||||
// bean.setConsumption(Constants.EMPTY_STRING);
|
||||
// bean.setVotestotal(Constants.EMPTY_STRING);
|
||||
// bean.setProvince(Constants.EMPTY_STRING);
|
||||
// bean.setCity(Constants.EMPTY_STRING);
|
||||
// bean.setLocation(Constants.EMPTY_STRING);
|
||||
// bean.setBirthday(Constants.EMPTY_STRING);
|
||||
// bean.setVip(new UserBean.Vip());
|
||||
// bean.setLiang(new UserBean.Liang());
|
||||
// bean.setCar(new UserBean.Car());
|
||||
// mEmptyUserBean = bean;
|
||||
// }
|
||||
// return mEmptyUserBean;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 设置位置信息
|
||||
*
|
||||
* @param lng 经度
|
||||
* @param lat 纬度
|
||||
*/
|
||||
public void setLngLat(double lng, double lat) {
|
||||
mLng = lng;
|
||||
mLat = lat;
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put(SpUtil.LOCATION_LNG, String.valueOf(lng));
|
||||
map.put(SpUtil.LOCATION_LAT, String.valueOf(lat));
|
||||
SpUtil.getInstance().setMultiStringValue(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置位置信息
|
||||
*
|
||||
* @param lng 经度
|
||||
* @param lat 纬度
|
||||
* @param province 省
|
||||
* @param city 市
|
||||
*/
|
||||
public void setLocationInfo(double lng, double lat, String province, String city, String district) {
|
||||
mLng = lng;
|
||||
mLat = lat;
|
||||
mProvince = province;
|
||||
mCity = city;
|
||||
mDistrict = district;
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put(SpUtil.LOCATION_LNG, String.valueOf(lng));
|
||||
map.put(SpUtil.LOCATION_LAT, String.valueOf(lat));
|
||||
map.put(SpUtil.LOCATION_PROVINCE, province);
|
||||
map.put(SpUtil.LOCATION_CITY, city);
|
||||
map.put(SpUtil.LOCATION_DISTRICT, district);
|
||||
SpUtil.getInstance().setMultiStringValue(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除定位信息
|
||||
*/
|
||||
public void clearLocationInfo() {
|
||||
mLng = 0;
|
||||
mLat = 0;
|
||||
mProvince = null;
|
||||
mCity = null;
|
||||
mDistrict = null;
|
||||
SpUtil.getInstance().removeValue(
|
||||
SpUtil.LOCATION_LNG,
|
||||
SpUtil.LOCATION_LAT,
|
||||
SpUtil.LOCATION_PROVINCE,
|
||||
SpUtil.LOCATION_CITY,
|
||||
SpUtil.LOCATION_DISTRICT);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本号
|
||||
*/
|
||||
public String getVersion() {
|
||||
if (TextUtils.isEmpty(mVersion)) {
|
||||
try {
|
||||
PackageManager manager = CommonAppContext.getInstance().getPackageManager();
|
||||
PackageInfo info = manager.getPackageInfo(PACKAGE_NAME1, 0);
|
||||
mVersion = info.versionName;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
public static boolean isYunBaoApp() {
|
||||
if (!TextUtils.isEmpty(PACKAGE_NAME1)) {
|
||||
return PACKAGE_NAME1.contains("com.xscm.qxlive");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取App名称
|
||||
*/
|
||||
public String getAppName() {
|
||||
if (TextUtils.isEmpty(mAppName)) {
|
||||
int res = CommonAppContext.getInstance().getResources().getIdentifier("app_name", "string", "com.xscm.qxlive");
|
||||
mAppName = WordUtil.getString(res);
|
||||
}
|
||||
return mAppName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取App图标的资源id
|
||||
*/
|
||||
public int getAppIconRes() {
|
||||
if (mAppIconRes == 0) {
|
||||
mAppIconRes = CommonAppContext.getInstance().getResources().getIdentifier("icon_app", "mipmap", "com.xscm.qxlive");
|
||||
}
|
||||
return mAppIconRes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取MetaData中的腾讯定位,地图的AppKey
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getTxMapAppKey() {
|
||||
if (mTxMapAppKey == null) {
|
||||
mTxMapAppKey = getMetaDataString("TencentMapSDK");
|
||||
}
|
||||
return mTxMapAppKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取MetaData中的腾讯定位,地图的AppSecret
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getTxMapAppSecret() {
|
||||
if (mTxMapAppSecret == null) {
|
||||
mTxMapAppSecret = getMetaDataString("TencentMapAppSecret");
|
||||
}
|
||||
return mTxMapAppSecret;
|
||||
}
|
||||
|
||||
|
||||
public static String getMetaDataString(String key) {
|
||||
String res = null;
|
||||
try {
|
||||
ApplicationInfo appInfo = CommonAppContext.getInstance().getPackageManager().getApplicationInfo(PACKAGE_NAME1, PackageManager.GET_META_DATA);
|
||||
res = appInfo.metaData.getString(key);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static boolean getMetaDataBoolean(String key) {
|
||||
boolean res = false;
|
||||
try {
|
||||
ApplicationInfo appInfo = CommonAppContext.getInstance().getPackageManager().getApplicationInfo(PACKAGE_NAME1, PackageManager.GET_META_DATA);
|
||||
res = appInfo.metaData.getBoolean(key);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static int getMetaDataInt(String key) {
|
||||
int res = 0;
|
||||
try {
|
||||
ApplicationInfo appInfo = CommonAppContext.getInstance().getPackageManager().getApplicationInfo(PACKAGE_NAME1, PackageManager.GET_META_DATA);
|
||||
res = appInfo.metaData.getInt(key, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static String getHost() {
|
||||
String host = getMetaDataString("SERVER_HOST");
|
||||
HEADER.put("referer", host);
|
||||
return host;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存用户等级信息
|
||||
*/
|
||||
// public void setLevel(String levelJson) {
|
||||
// if (TextUtils.isEmpty(levelJson)) {
|
||||
// return;
|
||||
// }
|
||||
// List<LevelBean> list = JSON.parseArray(levelJson, LevelBean.class);
|
||||
// if (list == null || list.size() == 0) {
|
||||
// return;
|
||||
// }
|
||||
// if (mLevelMap == null) {
|
||||
// mLevelMap = new SparseArray<>();
|
||||
// }
|
||||
// mLevelMap.clear();
|
||||
// for (LevelBean bean : list) {
|
||||
// mLevelMap.put(bean.getLevel(), bean);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 保存主播等级信息
|
||||
*/
|
||||
// public void setAnchorLevel(String anchorLevelJson) {
|
||||
// if (TextUtils.isEmpty(anchorLevelJson)) {
|
||||
// return;
|
||||
// }
|
||||
// List<LevelBean> list = JSON.parseArray(anchorLevelJson, LevelBean.class);
|
||||
// if (list == null || list.size() == 0) {
|
||||
// return;
|
||||
// }
|
||||
// if (mAnchorLevelMap == null) {
|
||||
// mAnchorLevelMap = new SparseArray<>();
|
||||
// }
|
||||
// mAnchorLevelMap.clear();
|
||||
// for (LevelBean bean : list) {
|
||||
// mAnchorLevelMap.put(bean.getLevel(), bean);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取用户等级
|
||||
*/
|
||||
// public LevelBean getLevel(int level) {
|
||||
// if (mLevelMap == null) {
|
||||
// String configString = SpUtil.getInstance().getStringValue(SpUtil.CONFIG);
|
||||
// if (!TextUtils.isEmpty(configString)) {
|
||||
// JSONObject obj = JSON.parseObject(configString);
|
||||
// setLevel(obj.getString("level"));
|
||||
// }
|
||||
// }
|
||||
// if (mLevelMap == null || mLevelMap.size() == 0) {
|
||||
// return null;
|
||||
// }
|
||||
// return mLevelMap.get(level);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取主播等级
|
||||
*/
|
||||
// public LevelBean getAnchorLevel(int level) {
|
||||
// if (mAnchorLevelMap == null) {
|
||||
// String configString = SpUtil.getInstance().getStringValue(SpUtil.CONFIG);
|
||||
// if (!TextUtils.isEmpty(configString)) {
|
||||
// JSONObject obj = JSON.parseObject(configString);
|
||||
// setAnchorLevel(obj.getString("levelanchor"));
|
||||
// }
|
||||
// }
|
||||
// if (mAnchorLevelMap == null || mAnchorLevelMap.size() == 0) {
|
||||
// return null;
|
||||
// }
|
||||
// return mAnchorLevelMap.get(level);
|
||||
// }
|
||||
|
||||
public String getGiftListJson() {
|
||||
return mGiftListJson;
|
||||
}
|
||||
|
||||
public void setGiftListJson(String getGiftListJson) {
|
||||
mGiftListJson = getGiftListJson;
|
||||
}
|
||||
|
||||
|
||||
public String getGiftDaoListJson() {
|
||||
return mGiftDaoListJson;
|
||||
}
|
||||
|
||||
public void setGiftDaoListJson(String getGiftDaoListJson) {
|
||||
mGiftDaoListJson = getGiftDaoListJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断某APP是否安装
|
||||
*/
|
||||
public static boolean isAppExist(String packageName) {
|
||||
if (!TextUtils.isEmpty(packageName)) {
|
||||
PackageManager manager = CommonAppContext.getInstance().getPackageManager();
|
||||
List<PackageInfo> list = manager.getInstalledPackages(0);
|
||||
for (PackageInfo info : list) {
|
||||
if (packageName.equalsIgnoreCase(info.packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean isLaunched() {
|
||||
return mLaunched;
|
||||
}
|
||||
|
||||
public void setLaunched(boolean launched) {
|
||||
mLaunched = launched;
|
||||
}
|
||||
|
||||
//app是否在前台
|
||||
public boolean isFrontGround() {
|
||||
return mFrontGround;
|
||||
}
|
||||
|
||||
//app是否在前台
|
||||
public void setFrontGround(boolean frontGround) {
|
||||
mFrontGround = frontGround;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
if (TextUtils.isEmpty(mDeviceId)) {
|
||||
String deviceId = SpUtil.getInstance().getStringValue(SpUtil.DEVICE_ID);
|
||||
if (TextUtils.isEmpty(deviceId)) {
|
||||
deviceId = DeviceUtils.getDeviceId();
|
||||
SpUtil.getInstance().setStringValue(SpUtil.DEVICE_ID, deviceId);
|
||||
}
|
||||
mDeviceId = deviceId;
|
||||
}
|
||||
LogUtils.e("getDeviceId---mDeviceId-----> " + mDeviceId);
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
public int getTopActivityType() {
|
||||
return mTopActivityType;
|
||||
}
|
||||
|
||||
public void setTopActivityType(int topActivityType) {
|
||||
mTopActivityType = topActivityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是基本功能模式
|
||||
*/
|
||||
public boolean isBaseFunctionMode() {
|
||||
return SpUtil.getInstance().getBooleanValue(SpUtil.BASE_FUNCTION_MODE, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置基本功能模式
|
||||
*/
|
||||
public void setBaseFunctionMode(boolean baseFunctionMode) {
|
||||
SpUtil.getInstance().setBooleanValue(SpUtil.BASE_FUNCTION_MODE, baseFunctionMode);
|
||||
}
|
||||
|
||||
public static String getHtmlUrl(String url) {
|
||||
if (!TextUtils.isEmpty(url) && url.startsWith(CommonAppConfig.HOST)) {
|
||||
if (!url.contains("?")) {
|
||||
url = StringUtil.contact(url, "?");
|
||||
}
|
||||
url = StringUtil.contact(url,
|
||||
"&uid=", CommonAppConfig.getInstance().getUid(),
|
||||
"&token=", CommonAppConfig.getInstance().getToken(),
|
||||
"&", Constants.LANGUAGE, "=", LanguageUtil.getInstance().getLanguage()
|
||||
);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public boolean isPrivateMsgSwitchOpen() {
|
||||
ConfigBean configBean = getConfig();
|
||||
if (configBean != null) {
|
||||
return configBean.getPriMsgSwitch() == 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
/**
|
||||
*@author
|
||||
*@data 2025/5/15
|
||||
*@description: 当没有数据时展示的view
|
||||
*/
|
||||
public class CommonEmptyView extends FrameLayout {
|
||||
private ImageView ivEmpty;
|
||||
private TextView tvEmptyText;
|
||||
private TextView tvOtherOperation;
|
||||
private LinearLayout layoutEmpty;
|
||||
private OtherOpListener otherOpListener;
|
||||
|
||||
public CommonEmptyView(@NonNull Context context) {
|
||||
super(context);
|
||||
initViews();
|
||||
}
|
||||
|
||||
public CommonEmptyView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initViews();
|
||||
}
|
||||
|
||||
public CommonEmptyView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initViews();
|
||||
}
|
||||
|
||||
|
||||
private void initViews() {
|
||||
LayoutInflater.from(getContext()).inflate(R.layout.layout_empty, this);
|
||||
ivEmpty = findViewById(R.id.iv_empty);
|
||||
tvEmptyText = findViewById(R.id.tv_empty_text);
|
||||
tvOtherOperation = findViewById(R.id.tv_other_operation);
|
||||
layoutEmpty = findViewById(R.id.layout_empty);
|
||||
tvOtherOperation.setOnClickListener(v -> otherOpListener.otherClick());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提示文字
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
public void setEmptyText(String text) {
|
||||
if (null != tvEmptyText) {
|
||||
tvEmptyText.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提示文字的颜色
|
||||
*/
|
||||
public void setTextColor(int color) {
|
||||
if (null != tvEmptyText) {
|
||||
tvEmptyText.setTextColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮文字
|
||||
*
|
||||
* @param txt
|
||||
*/
|
||||
public void setBtnText(String txt) {
|
||||
if (null != tvOtherOperation) {
|
||||
tvOtherOperation.setText(txt);
|
||||
tvOtherOperation.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置占位图
|
||||
*/
|
||||
public void setImg(int res) {
|
||||
ivEmpty.setImageResource(res);
|
||||
}
|
||||
|
||||
public void addOnOtherListener(OtherOpListener otherOpListener) {
|
||||
this.otherOpListener = otherOpListener;
|
||||
}
|
||||
|
||||
public interface OtherOpListener {
|
||||
void otherClick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
/**
|
||||
* Created by cxf on 2018/6/7.
|
||||
*/
|
||||
|
||||
public class Constants {
|
||||
public static final String URL = "url";
|
||||
public static final String PAYLOAD = "payload";
|
||||
public static final String SEX = "sex";
|
||||
public static final String NICK_NAME = "nickname";
|
||||
public static final String AVATAR = "avatar";
|
||||
public static final String SIGN = "sign";
|
||||
public static final String TO_UID = "toUid";
|
||||
public static final String FROM_LIVE_ROOM = "fromLiveRoom";
|
||||
public static final String FROM_LOGIN = "fromLogin";
|
||||
public static final String TO_NAME = "toName";
|
||||
public static final String STREAM = "stream";
|
||||
public static final String LIMIT = "limit";
|
||||
public static final String UID = "uid";
|
||||
public static final String TIP = "tip";
|
||||
public static final String EXIT = "exit";
|
||||
public static final String FIRST_LOGIN = "firstLogin";
|
||||
public static final String USER_BEAN = "userBean";
|
||||
public static final String CLASS_ID = "classID";
|
||||
public static final String CLASS_NAME = "className";
|
||||
public static final String CHECKED_ID = "checkedId";
|
||||
public static final String CHECKED_COIN = "checkedCoin";
|
||||
public static final String LIVE_DANMU_PRICE = "danmuPrice";
|
||||
public static final String COIN_NAME = "coinName";
|
||||
public static final String AT_NAME = "atName";
|
||||
public static final String LIVE_BEAN = "liveBean";
|
||||
public static final String LIVE_TYPE = "liveType";
|
||||
public static final String LIVE_KEY = "liveKey";
|
||||
public static final String LIVE_POSITION = "livePosition";
|
||||
public static final String LIVE_TYPE_VAL = "liveTypeVal";
|
||||
public static final String LIVE_UID = "liveUid";
|
||||
public static final String LIVE_PLAY_URL = "livePlayUrl";
|
||||
public static final String SHARE_UID = "shareUid";
|
||||
public static final String LIVE_STREAM = "liveStream";
|
||||
public static final String OPEN_PACK = "openPack";
|
||||
public static final String LIVE_HOME = "liveHome";
|
||||
public static final String LIVE_FOLLOW = "liveFollow";
|
||||
public static final String LIVE_NEAR = "liveNear";
|
||||
public static final String LIVE_CLASS_PREFIX = "liveClass_";
|
||||
public static final String LIVE_CLASS_RECOMMEND = "liveRecommend";
|
||||
public static final String LIVE_ADMIN_ROOM = "liveAdminRoom";
|
||||
public static final String HAS_GAME = "hasGame";
|
||||
public static final String OPEN_FLASH = "openFlash";
|
||||
public static final String LINK_MIC = "linkMic";
|
||||
public static final String SHARE_QR_CODE_FILE = "shareQrCodeFile.png";
|
||||
public static final String ANCHOR = "anchor";
|
||||
public static final String FOLLOW = "follow";
|
||||
public static final String DIAMONDS = "钻石";
|
||||
public static final String VOTES = "映票";
|
||||
public static final String SCORE = "积分";
|
||||
public static final String PAY_TYPE_ALI = "ali";
|
||||
public static final String PAY_TYPE_WX = "wx";
|
||||
public static final String PAY_TYPE_PAYPAL = "paypal";
|
||||
public static final String PAY_TYPE_BALANCE = "balance";
|
||||
public static final String PAY_BUY_COIN_ALI = "Charge.getAliOrder";
|
||||
public static final String PAY_BUY_COIN_WX = "Charge.getWxOrder";
|
||||
public static final String PAY_BUY_COIN_PAYPAL = "Charge.getBraintreePaypalOrder";
|
||||
|
||||
public static final String PACKAGE_NAME_ALI = "com.eg.android.AlipayGphone";//支付宝的包名
|
||||
public static final String PACKAGE_NAME_WX = "com.tencent.mm";//微信的包名
|
||||
public static final String PACKAGE_NAME_QQ = "com.tencent.mobileqq";//QQ的包名
|
||||
public static final String PACKAGE_NAME_GAODE_MAP = "com.autonavi.minimap";//高德地图包名
|
||||
public static final String PACKAGE_NAME_BAIDU_MAP = "com.baidu.BaiduMap";//百度地图包名
|
||||
public static final String PACKAGE_NAME_TX_MAP = "com.tencent.map";//腾讯地图包名
|
||||
public static final String LAT = "lat";
|
||||
public static final String LNG = "lng";
|
||||
public static final String ADDRESS = "address";
|
||||
public static final String SELECT_IMAGE_PATH = "selectedImagePath";
|
||||
public static final String COPY_PREFIX = "copy://";
|
||||
public static final String TEL_PREFIX = "tel://";
|
||||
public static final String AUTH_PREFIX = "auth://";
|
||||
public static final int GUARD_TYPE_NONE = 0;
|
||||
public static final int GUARD_TYPE_MONTH = 1;
|
||||
public static final int GUARD_TYPE_YEAR = 2;
|
||||
|
||||
public static final String DOWNLOAD_MUSIC = "downloadMusic";
|
||||
public static final String LINK = "link";
|
||||
public static final String REPORT = "report";
|
||||
public static final String SAVE = "save";
|
||||
public static final String DELETE = "delete";
|
||||
public static final String GOODS = "goods";
|
||||
public static final String MAX_COUNT = "maxCount";
|
||||
public static final String USE_CAMERA = "useCamera";
|
||||
public static final String USE_PREVIEW = "usePreview";
|
||||
|
||||
|
||||
public static final int SETTING_MODIFY_PWD = 15;
|
||||
public static final int SETTING_UPDATE_ID = 16;
|
||||
public static final int SETTING_CLEAR_CACHE = 18;
|
||||
public static final int SEX_MALE = 1;
|
||||
public static final int SEX_FEMALE = 2;
|
||||
public static final int FOLLOW_FROM_FOLLOW = 1002;
|
||||
public static final int FOLLOW_FROM_FANS = 1003;
|
||||
public static final int FOLLOW_FROM_SEARCH = 1004;
|
||||
public static final String IM_FROM_HOME = "imFromHome";
|
||||
//直播房间类型
|
||||
public static final int LIVE_TYPE_NORMAL = 0;//普通房间
|
||||
public static final int LIVE_TYPE_PWD = 1;//密码房间
|
||||
public static final int LIVE_TYPE_PAY = 2;//收费房间
|
||||
public static final int LIVE_TYPE_TIME = 3;//计时房间
|
||||
//主播直播间功能
|
||||
public static final int LIVE_FUNC_BEAUTY = 2001;//美颜
|
||||
public static final int LIVE_FUNC_CAMERA = 2002;//切换摄像头
|
||||
public static final int LIVE_FUNC_FLASH = 2003;//闪光灯
|
||||
public static final int LIVE_FUNC_MUSIC = 2004;//伴奏
|
||||
public static final int LIVE_FUNC_SHARE = 2005;//分享
|
||||
public static final int LIVE_FUNC_GAME = 2006;//游戏
|
||||
public static final int LIVE_FUNC_RED_PACK = 2007;//红包
|
||||
public static final int LIVE_FUNC_LINK_MIC = 2008;//连麦
|
||||
public static final int LIVE_FUNC_MIRROR = 2009;//镜像
|
||||
public static final int LIVE_FUNC_TASK = 2010;//每日任务
|
||||
public static final int LIVE_FUNC_LUCK = 2011;//幸运奖池
|
||||
public static final int LIVE_FUNC_PAN = 2012;//转盘
|
||||
public static final int LIVE_FUNC_MSG = 2013;//私信
|
||||
public static final int LIVE_FUNC_FACE = 2014;//表情
|
||||
public static final int LIVE_FUNC_LINK_MIC_AUD = 2015;//用户连麦
|
||||
public static final int LIVE_FUNC_REPORT = 2016;//举报
|
||||
//socket
|
||||
public static final String SOCKET_CONN = "conn";
|
||||
public static final String SOCKET_BROADCAST = "broadcastingListen";
|
||||
public static final String SOCKET_SEND = "broadcast";
|
||||
public static final String SOCKET_STOP_PLAY = "stopplay";//超管关闭直播间
|
||||
public static final String SOCKET_STOP_LIVE = "stopLive";//超管关闭直播间
|
||||
public static final String SOCKET_SEND_MSG = "SendMsg";//发送文字消息,点亮,用户进房间
|
||||
public static final String SOCKET_LIGHT = "light";//飘心
|
||||
public static final String SOCKET_SEND_GIFT = "SendGift";//送礼物
|
||||
public static final String SOCKET_SEND_BARRAGE = "SendBarrage";//发弹幕
|
||||
public static final String SOCKET_LEAVE_ROOM = "disconnect";//用户离开房间
|
||||
public static final String SOCKET_LIVE_END = "StartEndLive";//主播关闭直播
|
||||
public static final String SOCKET_SYSTEM = "SystemNot";//系统消息
|
||||
public static final String SOCKET_KICK = "KickUser";//踢人
|
||||
public static final String SOCKET_SHUT_UP = "ShutUpUser";//禁言
|
||||
public static final String SOCKET_SET_ADMIN = "setAdmin";//设置或取消管理员
|
||||
public static final String SOCKET_CHANGE_LIVE = "changeLive";//切换计时收费类型
|
||||
public static final String SOCKET_UPDATE_VOTES = "updateVotes";//门票或计时收费时候更新主播的映票数
|
||||
public static final String SOCKET_FAKE_FANS = "requestFans";//僵尸粉
|
||||
public static final String SOCKET_LINK_MIC = "ConnectVideo";//连麦
|
||||
public static final String SOCKET_LINK_MIC_ANCHOR = "LiveConnect";//主播连麦
|
||||
public static final String SOCKET_LINK_MIC_PK = "LivePK";//主播PK
|
||||
public static final String SOCKET_BUY_GUARD = "BuyGuard";//购买守护
|
||||
public static final String SOCKET_RED_PACK = "SendRed";//红包
|
||||
public static final String SOCKET_LUCK_WIN = "luckWin";//幸运礼物中奖
|
||||
public static final String SOCKET_PRIZE_POOL_WIN = "jackpotWin";//奖池中奖
|
||||
public static final String SOCKET_PRIZE_POOL_UP = "jackpotUp";//奖池升级
|
||||
public static final String SOCKET_GIFT_GLOBAL = "Sendplatgift";//全站礼物
|
||||
public static final String SOCKET_LIVE_GOODS_SHOW = "goodsLiveShow";//直播间展示商品
|
||||
public static final String SOCKET_LIVE_GOODS_SALE_NUM = "liveGoodsSaleNums";//直播间商品销量
|
||||
public static final String SOCKET_LIVE_GOODS_FLOAT = "shopGoodsLiveFloat";//直播间商品飘屏
|
||||
public static final String SOCKET_VOICE_ROOM = "voiceRoom";//语音聊天室
|
||||
public static final String SOCKET_LIVE_WARNING = "warning";//直播间警告
|
||||
public static final String SOCKET_XQTB_WIN = "xqtbWin";//星球探宝中奖后发送
|
||||
public static final String SOCKET_LUCKPAN_WIN = "xydzpWin";//幸运大转盘中奖后发送
|
||||
|
||||
//游戏socket
|
||||
public static final String SOCKET_GAME_ZJH = "startGame";//炸金花
|
||||
public static final String SOCKET_GAME_HD = "startLodumaniGame";//海盗船长
|
||||
public static final String SOCKET_GAME_NZ = "startCattleGame";//开心牛仔
|
||||
public static final String SOCKET_GAME_ZP = "startRotationGame";//幸运转盘
|
||||
public static final String SOCKET_GAME_EBB = "startShellGame";//二八贝
|
||||
|
||||
public static final int SOCKET_WHAT_CONN = 0;
|
||||
public static final int SOCKET_WHAT_DISCONN = 2;
|
||||
public static final int SOCKET_WHAT_BROADCAST = 1;
|
||||
//socket 用户类型
|
||||
public static final int SOCKET_USER_TYPE_NORMAL = 30;//普通用户
|
||||
public static final int SOCKET_USER_TYPE_ADMIN = 40;//房间管理员
|
||||
public static final int SOCKET_USER_TYPE_ANCHOR = 50;//主播
|
||||
public static final int SOCKET_USER_TYPE_SUPER = 60;//超管
|
||||
|
||||
//提现账号类型,1表示支付宝,2表示微信,3表示银行卡
|
||||
public static final int CASH_ACCOUNT_ALI = 1;
|
||||
public static final int CASH_ACCOUNT_WX = 2;
|
||||
public static final int CASH_ACCOUNT_BANK = 3;
|
||||
public static final String CASH_ACCOUNT_ID = "cashAccountID";
|
||||
public static final String CASH_ACCOUNT = "cashAccount";
|
||||
public static final String CASH_ACCOUNT_NAME = "cashAccountName";
|
||||
public static final String CASH_ACCOUNT_TYPE = "cashAccountType";
|
||||
|
||||
public static final String NOT_LOGIN_UID = "-9999";//未登录的uid
|
||||
public static final String NOT_LOGIN_TOKEN = "-9999";//未登录的token
|
||||
|
||||
|
||||
public static final String LANGUAGE = "language";
|
||||
public static final String LANG_EN = "en";//英文
|
||||
public static final String LANG_ZH = "zh-cn";//中文
|
||||
|
||||
public static final int PAYSUCESS = 0x0000;
|
||||
|
||||
public static final String FILE_PATH = "/YuTang";
|
||||
|
||||
|
||||
//接口地址
|
||||
public static final String SEND_CODE = "/api/Sms/send";
|
||||
public static final String LOGIN = "/api/Login/phone_code";
|
||||
public static final String USER_LOGIN = "/api/Login/user_login";
|
||||
//随机昵称
|
||||
public static final String UPLOAD_NICK_NAME = "/api/UserData/random_nickname";
|
||||
public static final String UPLOAD_USER_PIC = "/api/UserData/modify_user_sex_pic";
|
||||
|
||||
public static final String SWITCH_ACCOUNTS = "/api/Login/multi_account_login";
|
||||
|
||||
public static final String USER_UPDATE = "/api/UserData/modify_fist_user_info";
|
||||
|
||||
public static final String CHANGE_PASSWORD = "/api/UserData/modify_password";//设置密码
|
||||
|
||||
public static final String URL_LOGIN = "/api/Login/one_click_login";//一键登录
|
||||
public static final String URL_AUTH_CODE = "/api/Login/aliLogin";//支付宝登录
|
||||
public static final String URL_WX_CODE = "/api/Login/wechatLogin";//微信登录
|
||||
|
||||
public static final String AUTHORIZATION = "/api/Login/AlipayUserInfo";
|
||||
|
||||
public static final String REAL_NAME = "/api/UserData/real_name";//调用实名认证接口
|
||||
public static final String REAL_NAME_RESULT = "/api/UserData/real_name_result";//调用实名认证接口
|
||||
public static final String GET_EXPAND_COLUMN = "/api/UserZone/expand_zone";//扩列获取数据接口
|
||||
public static final String GET_OFFICIAL_NOTICE = "/api/UserMessage/get_user_message_cover_info";//系统封面消息
|
||||
public static final String GET_ALBUM_LIST = "/api/User/get_album_list";//相册列表
|
||||
public static final String CREATE_ALBUM = "/api/User/create_album";//创建相册
|
||||
public static final String EDIT_ALBUM = "/api/User/edit_album";//编辑相册
|
||||
public static final String GET_REWARD_LIST = "/api/UserZone/reward_list";//动态打赏列表
|
||||
public static final String GET_GIFT_LABEL = "/api/Gift/get_gift_label";//礼物标签
|
||||
public static final String GIFT_LIST = "/api/Gift/get_gift_list";//礼物列表
|
||||
public static final String TOPIC_LIST = "/api/UserZone/topic_list";//获取话题列表
|
||||
public static final String PUBLISH_ZONE = "/api/UserZone/publish_zone";//发布动态
|
||||
public static final String GET_CATEGORIES = "/api/UserZone/get_zone_topic";//动态引用的四条话题
|
||||
public static final String GET_CIRCLE_LIST = "/api/UserZone/zone_list";//语圈列表
|
||||
public static final String LIKE_ZONE = "/api/UserZone/like_zone";//语圈列表
|
||||
public static final String GET_COMMENT_LIST = "/api/UserZone/get_comment_list";//评论列表
|
||||
public static final String COMMENT_ZONE = "/api/UserZone/comment_zone";//评论列表
|
||||
public static final String TOPIC_ID = "/api/UserZone/get_zone_topic_list";//热门话题列表
|
||||
public static final String ZONE_DETAIL = "/api/UserZone/zone_detail";//语圈详情
|
||||
public static final String DELETE_COMMENT = "/api/UserZone/delete_zone_comment";//删除子评论
|
||||
public static final String DELETE_ZONE = "/api/UserZone/delete_zone";//删除动态
|
||||
public static final String FORGOT_PASSWORD = "/api/Login/forgot_password";//忘记密码
|
||||
public static final String CLEAR_LOGIN_INFO = "/api/Login/logout";//忘记密码
|
||||
public static final String CANCEL = "/api/Login/cancel";//注销账号
|
||||
public static final String GET_MY_INFO = "/api/User/get_user_info";//点击我的获取个人数据
|
||||
public static final String GET_USER_HOME = "/api/User/get_user_home";//点击获取个人数据
|
||||
|
||||
public static final String ED_USER_INFO = "/api/User/edit_user_info";//编辑信息
|
||||
public static final String ED_USER_BG = "/api/User/edit_user_bg";//编辑背景图片
|
||||
public static final String GET_USER_TAG_LIST = "/api/User/get_user_tag_list";//获取用户标签
|
||||
public static final String GET_USER_HOME_ZONE = "/api/User/user_home_zone";//获取用户动态
|
||||
public static final String GET_LIKE_LIST = "/api/UserZone/like_list";//获取点赞列表
|
||||
public static final String POST_GZ = "/api/User/follow";//关注、回关、取关
|
||||
public static final String GET_ALBUM_DETAIL = "/api/User/get_album_detail";//相册详情
|
||||
public static final String UP_ALBUM = "/api/User/add_album_content";//添加相册图片
|
||||
public static final String MOVE_ALBUM = "/api/User/move_album_images";//移动相册图片
|
||||
public static final String DELETE_ALBUM_IMAGE = "/api/User/delete_album_images";//删除图片
|
||||
public static final String DELETE_ALBUM = "/api/User/delete_album";//删除相册
|
||||
public static final String LIKE_ALBUM = "/api/User/like_album";//相册点赞
|
||||
public static final String GET_PERSONALTY = "/api/Decorate/get_type_list";//装扮类型列表
|
||||
public static final String GET_DECORATE = "/api/Decorate/user_decorate";//装扮详情
|
||||
public static final String SET_USER_DECORATE = "/api/Decorate/set_user_decorate";//用户装扮
|
||||
public static final String JOIN_ROOM = "/api/Room/join_room";//加入房间
|
||||
|
||||
public static final String UPDATEPASSWORD = "/api/room/setRoomPassword";//更新房间秘密啊
|
||||
public static final String GET_ROOM_ONLINE = "/api/Room/room_online_list";//房间在线列表
|
||||
public static final String GET_SJ_ROOM_NAME = "/api/Room/room_random_name";//随机房间名称
|
||||
public static final String CHECK_TXT = "/api/Room/create_room";//创建房间
|
||||
public static final String GET_MY_ROOM = "/api/Room/my_associated_room";//我的房间
|
||||
public static final String GET_MY_FOOT = "/api/Room/user_room_history_list";//我的足迹
|
||||
public static final String GET_TOP_ROOM = "/api/Index/room_list";//首页房间列表,置顶,不传lable_id,房间列表,is_top传1
|
||||
public static final String GET_ROOM_TYPE = "/api/Index/room_type_list";//房间分类列表
|
||||
public static final String GET_GIVE_GIFT = "/api/Gift/chat_gift_send";//聊天送礼物
|
||||
public static final String GET_WALLET = "/api/UserWallet/wallet";//钱包
|
||||
public static final String GET_ROOM_GIFT = "/api/Room/room_give_gift";//直播间送礼
|
||||
public static final String GET_ROOM_USER = "/api/Room/room_user_home";//房间内点击头像
|
||||
public static final String APPLY_PIT = "/api/RoomPit/apply_pit";//申请上麦
|
||||
public static final String DOWN_PIT = "/api/RoomPit/down_pit";//下麦
|
||||
public static final String ADDRESS_IP = "/api/User/update_user_ip";//修改ip地址
|
||||
public static final String REWARD_ZONE = "/api/UserZone/reward_zone";//动态打赏礼物
|
||||
public static final String POST_CHANGE_ROOM = "/api/RoomPit/change_room_up_pit_type";//修改上麦模式
|
||||
|
||||
public static final String POST_APPLY_LIST = "/api/RoomPit/apply_pit_list";//申请上麦列表
|
||||
public static final String SET_ROOM_APPLY = "/api/RoomPit/set_room_pit_apply_help_gift";//设置房间插麦礼物
|
||||
public static final String CLEAR_APPLY = "/api/RoomPit/clear_apply_pit_list";//清空申请列表
|
||||
public static final String AGREE_PIT = "/api/RoomPit/agree_pit";//同意上麦
|
||||
public static final String REFUSE_PIT = "/api/RoomPit/refuse_pit";//拒绝上麦
|
||||
public static final String HELP_APPLY = "/api/RoomPit/help_apply_pit";//上麦助力
|
||||
public static final String POST_APPLY_SONG = "/api/RoomSong/apply_song";//申请点歌
|
||||
public static final String POST_AGREE_SONG = "/api/RoomSong/agree_song";//同意、拒绝点歌
|
||||
public static final String POST_SONG_LIST = "/api/RoomSong/song_list";//获取已点歌曲
|
||||
public static final String POST_SONG = "/api/RoomSong/song";//点歌
|
||||
public static final String POST_UP_SONG = "/api/RoomSong/up_song";//移动歌曲
|
||||
public static final String POST_END_SONG = "/api/RoomSong/end_song";//结束本场歌
|
||||
public static final String POST_ROOM_INFO = "/api/Room/room_info";//房间信息
|
||||
public static final String GET_CHARM_RANK = "/api/RoomSong/get_charm_rank";//房间魅力排行榜
|
||||
public static final String CHANGE_SONG = "/api/RoomSong/change_song";//切歌
|
||||
public static final String POST_HOST_LIST = "/api/Room/host_list";//主持人、管理员列表
|
||||
public static final String POST_SEARCH = "/api/Search/search";//搜索, type:1用户,2房间,3公会
|
||||
public static final String SET_PRESIDED_RATIO = "/api/Room/set_host_profit";//设置主持人收益
|
||||
public static final String GET_PRESIDED_RATIO = "/api/Room/set_host";//操作主持、管理,type:1主持。2管理 is_add:1添加,2删除
|
||||
public static final String POST_ROOM_HOST_PIT = "/api/RoomPit/host_user_pit";//抱麦/踢麦 type:1抱麦,2踢麦 当在在线列表中,点击抱麦的时候,是没有
|
||||
public static final String POST_SET_MUTE_PIT = "api/Room/set_mute";// 禁言 禁麦 \解禁 is_mute 1-不让打字,2-不让说话,3-可打字,4-可说话
|
||||
public static final String POST_SET_LOCK_PIT = "/api/Room/set_lock_pit";//锁麦、解禁 is_lock:0未禁麦,1已禁
|
||||
public static final String GET_ROOM_BJ = "/api/Room/room_bg_list";//获取房间背景
|
||||
public static final String POST_SET_UPLOAD_BG_IMG = "/api/Room/upload_bg_img";//上传、删除背景图片
|
||||
public static final String POST_EDIT_ROOM = "/api/Room/edit_room";//编辑房间信息
|
||||
|
||||
public static final String POST_KICK_OUT_ROOM = "/api/Room/kick_out_room";//踢出房间
|
||||
public static final String POST_QUIT_ROOM = "/api/Room/quit_room";//退出房间
|
||||
public static final String ADD_BLACK_LIST = "/api/User/add_blacklist";//添加黑名单
|
||||
public static final String POST_FOLLOW_LIST = "/api/User/get_user_follow_list";//关注列表
|
||||
public static final String POST_FANS_LIST = "/api/User/get_user_fans_list";//粉丝列表
|
||||
public static final String POST_BLACK_LIST = "/api/User/get_blacklist";//黑名单
|
||||
public static final String POST_LOCK_MI_LIST = "/api/User/get_look_me_list";//看过我的,访客
|
||||
public static final String REMOVE_BLACK_LIST = "/api/User/remove_blacklist";//移除黑名单
|
||||
public static final String POST_CHANGE_ROOM_TYPE = "/api/Room/change_room_type";//修改房间类型
|
||||
public static final String POST_ROOM_RELATION_LIST = "/api/RoomAuction/room_relation_list";//房间关系列表
|
||||
public static final String POST_ROOM_AUCTION = "/api/RoomAuction/room_auction";//房间竞拍开始
|
||||
public static final String POST_AUCTION_DELAY = "/api/RoomAuction/room_auction_delay";//延时
|
||||
public static final String POST_AUCTION_END = "/api/RoomAuction/room_auction_end";//结束
|
||||
public static final String POST_ROOM_AUCTION_TIME = "/api/RoomAuction/room_auction_time";//真爱拍选礼物后计算天数
|
||||
public static final String POST_ROOM_AUCTION_JOIN = "/api/RoomAuction/room_auction_join";//参与竞拍
|
||||
public static final String POST_AUCTION_MODE = "/api/RoomAuction/room_auction_mode";//修改房间竞拍模式
|
||||
public static final String POST_ROOM_AUCTION_LIST = "/api/RoomAuction/room_auction_list";//房间竞拍列表
|
||||
public static final String GET_MY_CP_ROOM_LIST = "/api/Room/my_cp_room";//我的Cp房
|
||||
public static final String POST_SEARCH_PK_ROOM = "/api/RoomPk/search_pk_room";//搜索 或推荐的pk房间
|
||||
public static final String POST_SEND_PK = "/api/RoomPk/send_pk";//发起PK邀请
|
||||
public static final String ACCEPT_PK = "/api/RoomPk/accept_pk";//接受/拒绝 PK邀请
|
||||
public static final String POST_REFUSE_PK = "/api/RoomPk/refuse_pk";//不再接受 PK is_pk 1-接受,2-拒绝
|
||||
public static final String POST_END_PK = "/api/RoomPk/end_pk";//pk结束、断开链接,type:1:时间到后系统调用,2:没有开始前点击结束 3:开始后中途结束
|
||||
public static final String POST_START_PK = "/api/RoomPk/start_pk";//开始PK
|
||||
public static final String POST_CHARM_LIST = "/api/Room/room_turnover_detail";//房间流水明细
|
||||
public static final String GET_SUBSIDY = "/api/Room/room_ubsidy";//房间补贴
|
||||
public static final String POST_MY_ROOM_BEAN = "/api/Index/index_banner";//首页轮播图
|
||||
public static final String GET_RECHARGE = "/api/UserWallet/can_recharge_list";//可选充值金额列表
|
||||
public static final String POST_APPLY_Pay = "/api/Payment/app_pay";//APP支付
|
||||
public static final String POST_LOG_LIST = "/api/UserWallet/log_list";//金币(钻石)明细
|
||||
public static final String GET_REAL_NAME = "/api/UserData/real_name_info";//实名认证后的信息
|
||||
public static final String SEND_HEADLINE = "/api/Room/send_headline";//发(抢)头条
|
||||
public static final String CURRENT_HEADLINE = "/api/Room/current_headline";//当前发(抢)头条需要的数据
|
||||
public static final String POST_BIND_TYPE = "/api/Bind/bindType";//绑定方式及状态
|
||||
public static final String EARNINGS_NUM = "/api/UserWallet/exchange_coin";//收益(钻石)兑换金币
|
||||
public static final String GET_WALLET_CONFIG = "/api/UserWallet/get_wallet_config";//钱包相关配置接口
|
||||
public static final String POST_BIND = "/api/Bind/bind";//绑定(支付宝、银行卡、微信)
|
||||
public static final String POST_WITHDRAWAL = "/api/UserWithdrawal/withdrawal";//用户提现
|
||||
public static final String POST_WEALTH_RANKING = "/api/Ranking/wealth_ranking";//财富、魅力榜
|
||||
public static final String POST_ROOM_RANKING = "/api/Ranking/room_ranking";//房间榜
|
||||
public static final String GUILD_RANKING = "/api/Ranking/guild_ranking";//公会榜
|
||||
public static final String POST_LOVE_RANKING = "/api/Ranking/love_ranking";//真爱榜
|
||||
public static final String GET_TASKS_LIHEN = "/api/Dailytasks/dailyTasksList";//每日任务列表
|
||||
public static final String GET_DAILY_TASK_BOX = "/api/Dailytasks/dailyTasksBoxRecord";//礼盒记录
|
||||
public static final String dailyTasksOpenBox = "/api/Dailytasks/dailyTasksOpenBox";//开启礼盒
|
||||
public static final String GET_MY_BAG_DATA = "/api/UserGiftPack/get_gift_pack_income";//背包收入
|
||||
public static final String GET_MY_BAG_OUTCOME = "/api/UserGiftPack/get_gift_pack_outcome";//背包礼物支出列表
|
||||
public static final String GET_GIFT_PACK = "/api/UserGiftPack/get_gift_pack_list";//获取背包礼物列表
|
||||
public static final String GET_WITHDRAWAL_LIST = "/api/UserWithdrawal/withdrawal_list";//提现记录
|
||||
public static final String POST_ROOM_RANK = "/api/Room/room_rank";//房间排行榜(财富、魅力)
|
||||
public static final String GET_APP_UPDATE = "/api/Version/get_app_version";//版本更新
|
||||
public static final String POST_CLEAR_USER_CHARM = "/api/Room/clear_user_charm";//清除魅力值
|
||||
public static final String POST_MESSAGE_LIST = "/api/UserMessage/get_user_message_list";//消息列表
|
||||
public static final String POST_USER_WALL = "/api/User/user_gift_wall";//礼物墙
|
||||
public static final String POST_USER_OLINE_STATUS = "/api/Room/user_online_status";//用户在线状态
|
||||
public static final String DELETE_ROOM_HISTORY = "/api/Room/delete_room_history";//删除历史足迹
|
||||
public static final String POST_GIVE_COIN = "/api/User/give_coin";//转币功能
|
||||
public static final String POST_RELATION_CARD = "/api/Room/relation_card";//获取关系列表
|
||||
public static final String POST_TOP_RELATION_CARD = "/api/Room/top_relation_card";//置顶关系卡片
|
||||
public static final String POST_DELETE_RELATION_CARD = "/api/Room/delete_relation_card";//删除关系
|
||||
public static final String GET_FIRST_CHARGE = "/api/Activities/first_charge_gift_permission";//首充好礼弹框权限
|
||||
public static final String GET_FIRST_CHARGE_GIFT = "/api/Activities/first_charge_gift";//首充好礼列表接口
|
||||
public static final String dailyTasksReceive = "/api/Dailytasks/dailyTasksReceive";//领取每日任务奖励
|
||||
public static final String postRoomSwToken = "/api/Room/update_user_sw_token";//获取用户声网token
|
||||
public static final String dailyTasksComplete = "/api/Dailytasks/dailyTasksComplete";//领取每日任务奖励
|
||||
public static final String POST_CANCEL_USER_DECORATE = "/api/Decorate/cancel_user_decorate";//取消装扮
|
||||
public static final String GET_THEME_DATA = "/api/Theme/get_theme_data";//主题接口
|
||||
public static final String START_FRIEND = "/api/Friend/start_friend";//点击开始按钮 交友房
|
||||
public static final String DELAY = "/api/Friend/delay";//点击延时 交友房
|
||||
public static final String END_FRIEND = "/api/Friend/end_friend";//点击结束 交友房
|
||||
public static final String CREATE_RELATION = "/api/Friend/create_relation";//卡关系 (创建关系) 交友房
|
||||
|
||||
|
||||
|
||||
public static final String MODIFY_MOBILE = "/api/UserData/modify_mobile";//手机换绑
|
||||
public static final String BIND_MOBILE = "/api/UserData/bind_mobile";//手机绑定
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
|
||||
public class CustomBottomSheetDialog extends BottomSheetDialog {
|
||||
public CustomBottomSheetDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
/**
|
||||
*@author
|
||||
*@data 2025/5/15
|
||||
*@description: 输入框添加字数文字提示
|
||||
*/
|
||||
public class CustomEditText extends LinearLayout {
|
||||
|
||||
private EditText editText;
|
||||
private TextView lengthTextView;
|
||||
private int maxLength = 100; // 默认最大长度
|
||||
|
||||
public CustomEditText(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CustomEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
// 加载布局文件
|
||||
LayoutInflater.from(context).inflate(R.layout.custom_edit_text_layout, this, true);
|
||||
|
||||
// 找到 EditText 和 TextView 的引用
|
||||
editText = findViewById(R.id.edit_text);
|
||||
lengthTextView = findViewById(R.id.length_text_view);
|
||||
|
||||
// 初始化监听器
|
||||
editText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
updateLengthDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
// 设置默认的最大长度
|
||||
setMaxLength(maxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大长度
|
||||
*/
|
||||
public void setMaxLength(int maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
|
||||
updateLengthDisplay(); // 初始化显示
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新显示的字数
|
||||
*/
|
||||
private void updateLengthDisplay() {
|
||||
int currentLength = editText.getText().length();
|
||||
lengthTextView.setText(currentLength + "/" + maxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提示文本
|
||||
*/
|
||||
public void setHint(String hint) {
|
||||
editText.setHint(hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前文本
|
||||
*/
|
||||
public String getText() {
|
||||
return editText.getText().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文本
|
||||
*/
|
||||
public void setText(String text) {
|
||||
editText.setText(text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.petterp.floatingx.listener.control.IFxControl;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.databinding.RoomDialogMusicWindowOpenBinding;
|
||||
import com.xscm.moduleutil.interfaces.OnMusicItemClickListener;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
import io.agora.musiccontentcenter.Music;
|
||||
|
||||
public class CustomMusicFloatingView {
|
||||
private final IFxControl fxControl;
|
||||
private final Context context;
|
||||
private RoomDialogMusicWindowOpenBinding binding;
|
||||
private OnMusicItemClickListener onItemClickListener;
|
||||
private boolean isInitialized = false;
|
||||
|
||||
public CustomMusicFloatingView(Context context, IFxControl fxControl) {
|
||||
this.context = context;
|
||||
this.fxControl = fxControl;
|
||||
// this.binding = DataBindingUtil.inflate(LayoutInflater.from(context),
|
||||
// R.layout.room_dialog_music_window_open, null, false);
|
||||
}
|
||||
|
||||
public void initView() {
|
||||
View floatingView = fxControl.getView();
|
||||
if (floatingView == null || isInitialized) return;
|
||||
|
||||
// 替换为绑定方式获取控件
|
||||
binding = RoomDialogMusicWindowOpenBinding.bind(floatingView);
|
||||
|
||||
// 设置点击事件
|
||||
binding.ivMinx.setOnClickListener(v -> {
|
||||
if (onItemClickListener != null) {
|
||||
onItemClickListener.onMinimize();
|
||||
}
|
||||
});
|
||||
|
||||
binding.ivMusicPlayState.setOnClickListener(v -> {
|
||||
if (onItemClickListener != null) {
|
||||
if (binding.ivMusicPlayState.getTag() instanceof Boolean) {
|
||||
boolean isPlaying = (boolean) binding.ivMusicPlayState.getTag();
|
||||
if (isPlaying) {
|
||||
onItemClickListener.onPause();
|
||||
} else {
|
||||
onItemClickListener.onResume();
|
||||
}
|
||||
binding.ivMusicPlayState.setImageResource(isPlaying ?
|
||||
R.mipmap.room_music_win_puase : R.mipmap.room_music_win_start);
|
||||
binding.ivMusicPlayState.setTag(!isPlaying);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
binding.ivList.setOnClickListener(v -> {
|
||||
if (onItemClickListener != null) {
|
||||
onItemClickListener.onOpenList();
|
||||
}
|
||||
});
|
||||
|
||||
binding.ivNext.setOnClickListener(v -> {
|
||||
if (onItemClickListener != null) {
|
||||
onItemClickListener.onNext();
|
||||
}
|
||||
});
|
||||
isInitialized = true; // 只绑定一次
|
||||
}
|
||||
|
||||
public void show() {
|
||||
fxControl.show();
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
fxControl.hide();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
fxControl.cancel();
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnMusicItemClickListener listener) {
|
||||
this.onItemClickListener = listener;
|
||||
}
|
||||
|
||||
public void updatePlayState(boolean isPlaying) {
|
||||
// if (fxControl.getView() == null) return;
|
||||
// binding = RoomDialogMusicWindowOpenBinding.bind(fxControl.getView());
|
||||
binding.ivMusicPlayState.setImageResource(isPlaying ?
|
||||
R.mipmap.room_music_win_start : R.mipmap.room_music_win_puase);
|
||||
binding.ivMusicPlayState.setTag(isPlaying);
|
||||
}
|
||||
|
||||
public void updateTitle(String title, String singer) {
|
||||
if (fxControl.getView() == null) return;
|
||||
binding = RoomDialogMusicWindowOpenBinding.bind(fxControl.getView());
|
||||
binding.tvMusicTitle.setText(title);
|
||||
binding.tvSinger.setText(singer);
|
||||
}
|
||||
|
||||
public void onMusicEvent(Music event) {
|
||||
// updatePlayState(true);
|
||||
ImageUtils.loadHeadCC(event.getPoster(), binding.musicImg);
|
||||
binding.tvMusicTitle.setText(event.getName());
|
||||
binding.tvSinger.setText(event.getSinger());
|
||||
binding.musicM.setText("/" + formatSecondsToMinutes(event.getDurationS()));
|
||||
}
|
||||
|
||||
public void updateProgress(int progress) {
|
||||
updatePlayState(true);
|
||||
binding.musicT.setText(formatSecondsToMinutes(progress/1000));
|
||||
}
|
||||
public static String formatSecondsToMinutes(int totalSeconds) {
|
||||
int minutes = totalSeconds / 60;
|
||||
int seconds = totalSeconds % 60;
|
||||
return String.format("%02d:%02d", minutes, seconds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.utils.logger.Logger;
|
||||
import com.scwang.smartrefresh.layout.api.RefreshHeader;
|
||||
import com.scwang.smartrefresh.layout.api.RefreshKernel;
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout;
|
||||
import com.scwang.smartrefresh.layout.constant.RefreshState;
|
||||
import com.scwang.smartrefresh.layout.constant.SpinnerStyle;
|
||||
|
||||
/**
|
||||
* 项目名称 qipao-android
|
||||
* 包名:com.qpyy.module.index.widget
|
||||
* 创建人 王欧
|
||||
* 创建时间 2020/6/30 3:55 PM
|
||||
* 描述 describe
|
||||
*/
|
||||
public class CustomRefreshHeader extends ConstraintLayout implements RefreshHeader {
|
||||
ImageView mImageView;
|
||||
AnimationDrawable mAnimationDrawable;
|
||||
|
||||
public CustomRefreshHeader(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CustomRefreshHeader(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
LayoutInflater.from(context).inflate(R.layout.index_header_custom_refresh, this);
|
||||
mImageView = findViewById(R.id.image);
|
||||
mAnimationDrawable = (AnimationDrawable) mImageView.getBackground();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SpinnerStyle getSpinnerStyle() {
|
||||
return SpinnerStyle.Translate;
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void setPrimaryColors(int... colors) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onReleased(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {
|
||||
Logger.e("onReleased");
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onStartAnimator(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {
|
||||
Logger.e("onStartAnimator");
|
||||
//判断是否在运行
|
||||
if (!mAnimationDrawable.isRunning()) {
|
||||
//开启帧动画
|
||||
mAnimationDrawable.start();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public int onFinish(@NonNull RefreshLayout refreshLayout, boolean success) {
|
||||
if (mAnimationDrawable.isRunning()) {
|
||||
//开启帧动画
|
||||
mAnimationDrawable.stop();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupportHorizontalDrag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.blankj.utilcode.util.BarUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.databinding.ViewCustomTopBarBinding;
|
||||
|
||||
public class CustomTopBar extends ConstraintLayout {
|
||||
private OnCallBackRightIcon onCallBackRightIcon;
|
||||
private OnCallBackRightIcon2 onCallBackRightIcon2;
|
||||
private ViewCustomTopBarBinding mBinding;
|
||||
|
||||
public CustomTopBar(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public CustomTopBar(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CustomTopBar(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.view_custom_top_bar, this, true);
|
||||
mBinding.ivBack.setOnClickListener(v -> {
|
||||
if (getContext() instanceof Activity) {
|
||||
((Activity) getContext()).finish();
|
||||
}
|
||||
});
|
||||
mBinding.ivIntent.setOnClickListener(v -> onCallBackRightIcon.onIntent());
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomTopBar);
|
||||
String title = typedArray.getString(R.styleable.CustomTopBar_TopBarTitle);
|
||||
typedArray.recycle();
|
||||
setTitle(title);
|
||||
setPadding(0, BarUtils.getStatusBarHeight(), 0, 0);
|
||||
mBinding.tvRight.setOnClickListener(v -> onCallBackRightIcon2.onIntent());
|
||||
}
|
||||
|
||||
public ImageView getIvBack() {
|
||||
return mBinding.ivBack;
|
||||
}
|
||||
|
||||
public TextView getTvRight() {
|
||||
return mBinding.tvRight;
|
||||
}
|
||||
|
||||
public TextView getTvTitle() {
|
||||
return mBinding.tvTitle;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
mBinding.tvTitle.setText(title);
|
||||
}
|
||||
|
||||
public void setRightText(String txt) {
|
||||
mBinding.tvRight.setText(txt);
|
||||
}
|
||||
|
||||
public void setRightColor(int color) {
|
||||
mBinding.tvRight.setTextColor(color);
|
||||
}
|
||||
|
||||
public void setRightSize(int size) {
|
||||
mBinding.tvRight.setTextSize(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置右图片
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
public void setRightIcon(int res) {
|
||||
mBinding.ivIntent.setImageResource(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片右边距
|
||||
*
|
||||
* @param i
|
||||
*/
|
||||
public void setImgPaddingRight(int i) {
|
||||
mBinding.ivIntent.setPadding(0, 0, i, 0);
|
||||
}
|
||||
|
||||
public void setRightTxtVisible(boolean b) {
|
||||
mBinding.tvRight.setVisibility(b ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void setRightImgVIsible(boolean b) {
|
||||
mBinding.ivIntent.setVisibility(b ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mBinding.tvTitle.setTextColor(color);
|
||||
mBinding.ivBack.setColorFilter(color);
|
||||
}
|
||||
|
||||
public void addIntentListener(OnCallBackRightIcon onCallBackRightIcon) {
|
||||
this.onCallBackRightIcon = onCallBackRightIcon;
|
||||
}
|
||||
|
||||
public interface OnCallBackRightIcon {
|
||||
void onIntent();
|
||||
}
|
||||
|
||||
public void addIntentListener2(OnCallBackRightIcon2 onCallBackRightIcon) {
|
||||
this.onCallBackRightIcon2 = onCallBackRightIcon;
|
||||
}
|
||||
|
||||
public interface OnCallBackRightIcon2 {
|
||||
void onIntent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.xscm.moduleutil.utils.logger.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* 项目名称 qipao-android
|
||||
* 包名:com.yutang.xqipao.utils.view
|
||||
* 创建人 王欧
|
||||
* 创建时间 2020/4/9 12:46 PM
|
||||
* 描述 describe
|
||||
*/
|
||||
public class DecorationHeadView extends ConstraintLayout {
|
||||
private ImageView mRiv;
|
||||
private ImageView mIvFrame;
|
||||
private ImageView mIvSex;
|
||||
private ImageView mIvOnline;
|
||||
|
||||
public DecorationHeadView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public DecorationHeadView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public DecorationHeadView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
LayoutInflater.from(context).inflate(R.layout.common_view_decoration_head, this, true);
|
||||
mRiv = findViewById(R.id.riv);
|
||||
mIvFrame = findViewById(R.id.iv_frame);
|
||||
mIvSex = findViewById(R.id.iv_sex);
|
||||
mIvOnline = findViewById(R.id.iv_online);
|
||||
}
|
||||
|
||||
public void setData(String headPicture, String framePicture, String sex) {
|
||||
Logger.e(headPicture, framePicture, sex);
|
||||
if (!TextUtils.isEmpty(headPicture)) {
|
||||
ImageUtils.loadHeadCC(headPicture, mRiv);
|
||||
}
|
||||
if (TextUtils.isEmpty(framePicture)) {
|
||||
mIvSex.setVisibility(VISIBLE);
|
||||
if (!TextUtils.isEmpty(sex)) {
|
||||
if (sex.equals("1")) {
|
||||
mIvSex.setBackgroundResource(R.mipmap.nan);
|
||||
} else {
|
||||
mIvSex.setBackgroundResource(R.mipmap.nv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
}
|
||||
ImageUtils.loadImageView(framePicture, mIvFrame);
|
||||
}
|
||||
|
||||
public void setOnline(boolean isOnline) {
|
||||
mIvOnline.setVisibility(VISIBLE);
|
||||
mIvOnline.setImageResource(isOnline ? R.mipmap.me_online_icon : R.mipmap.me_icon_unchecked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public class DoubleTimePickerBottomSheet extends BottomSheetDialogFragment {
|
||||
|
||||
private OnTimeRangeSelectedListener listener;
|
||||
|
||||
public interface OnTimeRangeSelectedListener {
|
||||
void onTimeRangeSelected(Date startDate, Date endDate);
|
||||
}
|
||||
|
||||
public void setOnTimeRangeSelectedListener(OnTimeRangeSelectedListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
return new CustomBottomSheetDialog(requireContext());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.dialog_double_time_picker, container, false);
|
||||
|
||||
// 初始化 TabHost
|
||||
TabHost tabHost = view.findViewById(R.id.tabHost);
|
||||
tabHost.setup();
|
||||
|
||||
tabHost.addTab(tabHost.newTabSpec("start").setIndicator("开始时间").setContent(R.id.start_time_tab));
|
||||
tabHost.addTab(tabHost.newTabSpec("end").setIndicator("结束时间").setContent(R.id.end_time_tab));
|
||||
|
||||
// 初始化 Wheel Views
|
||||
WheelDatePicker wheelStartDate = view.findViewById(R.id.wheel_start_date);
|
||||
WheelTimePicker wheelStartTime = view.findViewById(R.id.wheel_start_time);
|
||||
WheelDatePicker wheelEndDate = view.findViewById(R.id.wheel_end_date);
|
||||
WheelTimePicker wheelEndTime = view.findViewById(R.id.wheel_end_time);
|
||||
|
||||
// 默认设置当前时间
|
||||
Calendar startCal = Calendar.getInstance();
|
||||
wheelStartDate.init(startCal.get(Calendar.YEAR), startCal.get(Calendar.MONTH), startCal.get(Calendar.DAY_OF_MONTH));
|
||||
wheelStartTime.init(startCal.get(Calendar.HOUR_OF_DAY), startCal.get(Calendar.MINUTE),startCal.get(Calendar.SECOND));
|
||||
|
||||
// 设置默认结束时间略晚于开始时间
|
||||
Calendar endCal = (Calendar) startCal.clone();
|
||||
endCal.add(Calendar.HOUR, 1);
|
||||
wheelEndDate.init(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH), endCal.get(Calendar.DAY_OF_MONTH));
|
||||
wheelEndTime.init(endCal.get(Calendar.HOUR_OF_DAY), endCal.get(Calendar.MINUTE),endCal.get(Calendar.SECOND));
|
||||
|
||||
// 取消按钮
|
||||
view.findViewById(R.id.btn_cancel).setOnClickListener(v -> dismiss());
|
||||
|
||||
// 确定按钮
|
||||
view.findViewById(R.id.btn_sure).setOnClickListener(v -> {
|
||||
Calendar start = Calendar.getInstance();
|
||||
start.set(wheelStartDate.getYear(), wheelStartDate.getMonth(), wheelStartDate.getDay(),
|
||||
wheelStartTime.getHour(), wheelStartTime.getMinute(), wheelStartTime.getSecond());
|
||||
|
||||
Calendar end = Calendar.getInstance();
|
||||
end.set(wheelEndDate.getYear(), wheelEndDate.getMonth(), wheelEndDate.getDay(),
|
||||
wheelEndTime.getHour(), wheelEndTime.getMinute(), wheelEndTime.getSecond());
|
||||
|
||||
if (end.getTime().before(start.getTime())) {
|
||||
Toast.makeText(requireContext(), "结束时间不能早于开始时间", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener.onTimeRangeSelected(start.getTime(), end.getTime());
|
||||
}
|
||||
dismiss();
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewAnimationUtils;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.blankj.utilcode.util.ScreenUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
public class DropView extends LinearLayout {
|
||||
public DropView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public DropView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public DropView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
void init() {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//设置初始位置
|
||||
int sh = ScreenUtils.getScreenHeight();
|
||||
int sw = ScreenUtils.getScreenWidth();
|
||||
setBackgroundResource(R.drawable.bg_home_drop_view);
|
||||
int y = (int) (0.5f * sh) - getHeight();
|
||||
int x = sw - getWidth();
|
||||
setTranslationX(x);
|
||||
setTranslationY(y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
boolean starDrap = false;
|
||||
float X1;
|
||||
float X2;
|
||||
float Y1;
|
||||
float Y2;
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||
if (starDrap) return true;
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
X1 = event.getX();
|
||||
Y1 = event.getY();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
X2 = event.getX();//当手指抬起时,再次获取屏幕位置的X值
|
||||
Y2 = event.getY();//同理
|
||||
Action(X1, X2, Y1, Y2);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
return starDrap;
|
||||
}
|
||||
|
||||
String TAG = "DropView";
|
||||
|
||||
public boolean Action(float X1, float X2, float Y1, float Y2) {
|
||||
float ComparedX = X2 - X1;//第二次的X坐标的位置减去第一次X坐标的位置,代表X坐标上的变化情况
|
||||
float ComparedY = Y2 - Y1;//同理
|
||||
//当X坐标的变化量的绝对值大于Y坐标的变化量的绝对值,以X坐标的变化情况作为判断依据
|
||||
//上下左右的判断,都在一条直线上,但手指的操作不可能划直线,所有选择变化量大的方向上的量
|
||||
//作为判断依据
|
||||
if (Math.abs(ComparedX) > 30 || Math.abs(ComparedY) > 30) {
|
||||
Log.i(TAG, "Action: 拖动");
|
||||
starDrap = true;
|
||||
setBackgroundResource(R.drawable.bg_home_drop_view);
|
||||
return true;
|
||||
} else {
|
||||
starDrap = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
setBackgroundResource(R.drawable.bg_home_drop_view);
|
||||
setTranslationX(getX() + (event.getX() - X1));
|
||||
setTranslationY(getY() + (event.getY() - Y1));
|
||||
X2 = event.getX();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
starDrap = false;
|
||||
int sw = ScreenUtils.getScreenWidth();
|
||||
Log.i(TAG, "onTouchEvent: " + sw + "," + X2);
|
||||
boolean isR = getTranslationX() + getWidth() / 2 >= sw / 2;//贴边方向
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "translationX", isR ? sw - getWidth() : 0f).setDuration(200);
|
||||
anim.start();
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void doRevealAnimation(View mPuppet, boolean flag) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
int[] vLocation = new int[2];
|
||||
getLocationInWindow(vLocation);
|
||||
int centerX = vLocation[0] + getMeasuredWidth() / 2;
|
||||
int centerY = vLocation[1] + getMeasuredHeight() / 2;
|
||||
|
||||
int height = ScreenUtils.getScreenHeight();
|
||||
int width = ScreenUtils.getScreenWidth();
|
||||
int maxRradius = (int) Math.hypot(height, width);
|
||||
Log.e("hei", maxRradius + "");
|
||||
|
||||
if (flag) {
|
||||
mPuppet.setVisibility(VISIBLE);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, maxRradius, 0);
|
||||
animator.setDuration(600);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
mPuppet.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
flag = false;
|
||||
} else {
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, 0, maxRradius);
|
||||
animator.setDuration(1000);
|
||||
animator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mPuppet.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.bumptech.glide.integration.webp.decoder.WebpDrawable;
|
||||
import com.xscm.moduleutil.bean.FaceBean;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ExpressionImgView extends AppCompatImageView {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private Runnable goneCmd = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public ExpressionImgView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ExpressionImgView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ExpressionImgView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
|
||||
public void addData(FaceBean faceBean) {
|
||||
setVisibility(VISIBLE);
|
||||
removeCallbacks(goneCmd);
|
||||
if (faceBean.getType() == 1) {
|
||||
String url = faceBean.getFace_spectial();
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return;
|
||||
}
|
||||
// GlideApp.with(mContext).load(url).listener(new RequestListener<Drawable>() {
|
||||
// @Override
|
||||
// public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
|
||||
// postDelayed(goneCmd, faceBean.getMillTime() == 0 ? 3000 : Math.max(faceBean.getMillTime(), 1000));
|
||||
// return false;
|
||||
// }
|
||||
// }).skipMemoryCache(true).into(this);
|
||||
} else {
|
||||
// GlideApp.with(mContext).load(String.format("http://soundriver.oss-cn-hangzhou.aliyuncs.com/custom/random%ss.webp", faceBean.getNumber())).into(new SimpleTarget<Drawable>() {
|
||||
// @Override
|
||||
// public void onResourceReady(@NonNull Drawable drawable, @Nullable Transition<? super Drawable> transition) {
|
||||
// if (drawable instanceof WebpDrawable) {
|
||||
// ExpressionImgView.this.setImageDrawable(drawable);
|
||||
// ((WebpDrawable) drawable).start();
|
||||
// ((WebpDrawable) drawable).setLoopCount(1);
|
||||
// ((WebpDrawable) drawable).registerAnimationCallback(new Animatable2Compat.AnimationCallback() {
|
||||
// @Override
|
||||
// public void onAnimationEnd(Drawable drawable) {
|
||||
// super.onAnimationEnd(drawable);
|
||||
// postDelayed(goneCmd, 500);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
private int getWebpPlayTime(Drawable resource) {
|
||||
WebpDrawable webpDrawable = (WebpDrawable) resource;
|
||||
try {
|
||||
Field gifStateField = WebpDrawable.class.getDeclaredField("state");
|
||||
gifStateField.setAccessible(true);
|
||||
Class gifStateClass = Class.forName("com.bumptech.glide.integration.webp.decoder.WebpDrawable$WebpState");
|
||||
Field gifFrameLoaderField = gifStateClass.getDeclaredField("frameLoader");
|
||||
gifFrameLoaderField.setAccessible(true);
|
||||
|
||||
Class gifFrameLoaderClass = Class.forName("com.bumptech.glide.integration.webp.decoder.WebpFrameLoader");
|
||||
Field gifDecoderField = gifFrameLoaderClass.getDeclaredField("webpDecoder");
|
||||
gifDecoderField.setAccessible(true);
|
||||
|
||||
Class gifDecoderClass = Class.forName("com.bumptech.glide.integration.webp.decoder.WebpDecoder");
|
||||
Object gifDecoder = gifDecoderField.get(gifFrameLoaderField.get(gifStateField.get(resource)));
|
||||
Method getDelayMethod = gifDecoderClass.getDeclaredMethod("getDelay", int.class);
|
||||
getDelayMethod.setAccessible(true);
|
||||
// 设置只播放一次
|
||||
// 获得总帧数
|
||||
int count = webpDrawable.getFrameCount();
|
||||
int delay = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// 计算每一帧所需要的时间进行累加
|
||||
delay += (int) getDelayMethod.invoke(gifDecoder, i);
|
||||
}
|
||||
return delay;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public void remove() {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
public class GifAvatarOvalView extends AppCompatImageView {
|
||||
public GifAvatarOvalView(@NonNull Context context) {
|
||||
super(context);
|
||||
init(null);
|
||||
}
|
||||
|
||||
public GifAvatarOvalView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
public GifAvatarOvalView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
protected int mWidth;
|
||||
protected int mHeight;
|
||||
protected float mRadius;
|
||||
|
||||
public static final float DEFAULT_BORDER_WIDTH = 0f;
|
||||
public static final int DEFAULT_BORDER_COLOR = Color.WHITE;
|
||||
protected float mBorderWidth = DEFAULT_BORDER_WIDTH;
|
||||
protected ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
|
||||
protected RectF mBorderRect = new RectF();
|
||||
protected Paint mBorderPaint;
|
||||
|
||||
private void init(@Nullable AttributeSet attrs) {
|
||||
if (attrs != null) {
|
||||
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.GifAvatarOvalView);
|
||||
mBorderWidth = a.getDimensionPixelSize(R.styleable.GifAvatarOvalView_gav_border_width, -1);
|
||||
if (mBorderWidth < 0) {
|
||||
mBorderWidth = DEFAULT_BORDER_WIDTH;
|
||||
}
|
||||
mBorderColor = a.getColorStateList(R.styleable.GifAvatarOvalView_gav_border_color);
|
||||
if (mBorderColor == null) {
|
||||
mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
mBorderPaint = new Paint();
|
||||
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
mBorderPaint.setAntiAlias(true);
|
||||
mBorderPaint.setColor(mBorderColor.getDefaultColor());
|
||||
mBorderPaint.setStrokeWidth(mBorderWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
mWidth = mHeight = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
|
||||
mRadius = mWidth / 2.0f;
|
||||
mBorderRect.set(0, 0, mWidth, mHeight);
|
||||
setMeasuredDimension(mWidth, mHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
setClipToOutline(true);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
Rect selfRect = new Rect(0, 0, mWidth, mHeight);
|
||||
outline.setRoundRect(selfRect, mRadius);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (mBorderWidth > 0) {
|
||||
canvas.drawOval(mBorderRect, mBorderPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBorderWidth(@DimenRes int resId) {
|
||||
setBorderWidth(getResources().getDimension(resId));
|
||||
}
|
||||
|
||||
public void setBorderWidth(float width) {
|
||||
if (mBorderWidth == width) { return; }
|
||||
mBorderWidth = width;
|
||||
mBorderPaint.setStrokeWidth(mBorderWidth);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setBorderColor(@ColorInt int color) {
|
||||
setBorderColor(ColorStateList.valueOf(color));
|
||||
}
|
||||
|
||||
public void setBorderColor(ColorStateList colors) {
|
||||
if (mBorderColor.equals(colors)) { return; }
|
||||
mBorderColor = (colors != null) ? colors : ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
|
||||
mBorderPaint.setColor(mBorderColor.getDefaultColor());
|
||||
if (mBorderWidth > 0) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class ImageSaveUtils {
|
||||
|
||||
public static void saveImg(Context context, Bitmap bitmap) {
|
||||
String fileName = "image_" + System.currentTimeMillis() + ".jpg";
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
|
||||
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
|
||||
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
|
||||
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
|
||||
try (OutputStream os = resolver.openOutputStream(uri)) {
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
|
||||
Toast.makeText(context, "图片已保存到相册", Toast.LENGTH_SHORT).show();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(context, "保存失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
// 发送广播通知系统刷新图库
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(uri);
|
||||
context.sendBroadcast(mediaScanIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class InfintLinearLayoutManager extends LinearLayoutManager {
|
||||
public InfintLinearLayoutManager(RecyclerView recyclerView) {
|
||||
super(recyclerView.getContext(), HORIZONTAL, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canScrollHorizontally() {
|
||||
return true; // 允许水平滑动
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
return super.scrollVerticallyBy(dy, recycler, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
// 获取最后一个和第一个可见 item 的位置
|
||||
int first = findFirstVisibleItemPosition();
|
||||
int last = findLastVisibleItemPosition();
|
||||
|
||||
if (first == 0 && dx > 0) {
|
||||
// 滑动到了最左边,现在往右滑,把最后一个 item 移动到前面复用
|
||||
View lastView = getChildAt(last);
|
||||
if (lastView != null) {
|
||||
layoutScrapView(lastView, last, -1);
|
||||
offsetChildrenHorizontal(-getDecoratedMeasuredWidth(lastView));
|
||||
return dx;
|
||||
}
|
||||
} else if (last == getItemCount() - 1 && dx < 0) {
|
||||
// 滑动到了最右边,现在往左滑,把第一个 item 移动到最后面复用
|
||||
View firstView = getChildAt(first);
|
||||
if (firstView != null) {
|
||||
layoutScrapView(firstView, first, +1);
|
||||
offsetChildrenHorizontal(+getDecoratedMeasuredWidth(firstView));
|
||||
return dx;
|
||||
}
|
||||
}
|
||||
|
||||
return super.scrollHorizontallyBy(dx, recycler, state);
|
||||
}
|
||||
|
||||
private void layoutScrapView(View view, int position, int direction) {
|
||||
removeView(view);
|
||||
|
||||
// 复用的 item 设置为新的位置
|
||||
int newPosition = direction > 0 ? getItemCount() - 1 : 0;
|
||||
addView(view, direction > 0 ? getChildCount() : 0);
|
||||
|
||||
// 更新 adapter 的 position
|
||||
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
|
||||
layoutDecoratedWithMargins(view,
|
||||
0,
|
||||
0,
|
||||
getDecoratedMeasuredWidth(view),
|
||||
getDecoratedMeasuredHeight(view));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
|
||||
|
||||
public class MarqueeTextView extends AppCompatTextView {
|
||||
|
||||
public MarqueeTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MarqueeTextView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
// 设置单行
|
||||
setSingleLine();
|
||||
// setMaxLines(1);
|
||||
//设置 Ellipsize,setMaxLines(1) 和 setEllipsize 冲突
|
||||
setEllipsize(TextUtils.TruncateAt.MARQUEE);
|
||||
//获取焦距
|
||||
setFocusable(true);
|
||||
//走马灯的重复次数,-1代表无限重复
|
||||
setMarqueeRepeatLimit(-1);
|
||||
//强制获得焦点
|
||||
setFocusableInTouchMode(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用于 EditText 存在时抢占焦点
|
||||
*/
|
||||
@Override
|
||||
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
|
||||
if (focused) {
|
||||
super.onFocusChanged(focused, direction, previouslyFocusedRect);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Window与Window间焦点发生改变时的回调
|
||||
* 解决 Dialog 抢占焦点问题
|
||||
*
|
||||
* @param hasWindowFocus
|
||||
*/
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
if (hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {//必须重写,且返回值是true,表示始终获取焦点
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Xfermode;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* 圆形头像
|
||||
*/
|
||||
@SuppressLint("AppCompatCustomView")
|
||||
public abstract class MaskedImage extends ImageView {
|
||||
|
||||
private static final Xfermode MASK_XFERMODE;
|
||||
private Bitmap mask;
|
||||
private Paint paint;
|
||||
|
||||
static {
|
||||
PorterDuff.Mode localMode = PorterDuff.Mode.DST_IN;
|
||||
MASK_XFERMODE = new PorterDuffXfermode(localMode);
|
||||
}
|
||||
|
||||
public MaskedImage(Context paramContext) {
|
||||
super(paramContext);
|
||||
}
|
||||
|
||||
public MaskedImage(Context paramContext, AttributeSet paramAttributeSet) {
|
||||
super(paramContext, paramAttributeSet);
|
||||
}
|
||||
|
||||
public MaskedImage(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
|
||||
super(paramContext, paramAttributeSet, paramInt);
|
||||
}
|
||||
|
||||
public abstract Bitmap createMask();
|
||||
|
||||
protected void onDraw(Canvas paramCanvas) {
|
||||
Drawable localDrawable = getDrawable();
|
||||
if (localDrawable == null)
|
||||
return;
|
||||
try {
|
||||
if (this.paint == null) {
|
||||
Paint localPaint1 = new Paint();
|
||||
this.paint = localPaint1;
|
||||
this.paint.setFilterBitmap(false);
|
||||
Paint localPaint2 = this.paint;
|
||||
Xfermode localXfermode1 = MASK_XFERMODE;
|
||||
@SuppressWarnings("unused")
|
||||
Xfermode localXfermode2 = localPaint2.setXfermode(localXfermode1);
|
||||
}
|
||||
float f1 = getWidth();
|
||||
float f2 = getHeight();
|
||||
int i = paramCanvas.saveLayer(0.0F, 0.0F, f1, f2, null, Canvas.ALL_SAVE_FLAG);
|
||||
int j = getWidth();
|
||||
int k = getHeight();
|
||||
localDrawable.setBounds(0, 0, j, k);
|
||||
localDrawable.draw(paramCanvas);
|
||||
if ((this.mask == null) || (this.mask.isRecycled())) {
|
||||
Bitmap localBitmap1 = createMask();
|
||||
this.mask = localBitmap1;
|
||||
}
|
||||
Bitmap localBitmap2 = this.mask;
|
||||
Paint localPaint3 = this.paint;
|
||||
paramCanvas.drawBitmap(localBitmap2, 0.0F, 0.0F, localPaint3);
|
||||
paramCanvas.restoreToCount(i);
|
||||
return;
|
||||
} catch (Exception localException) {
|
||||
StringBuilder localStringBuilder = new StringBuilder()
|
||||
.append("Attempting to draw with recycled bitmap. View ID = ");
|
||||
System.out.println("localStringBuilder==" + localStringBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.databinding.RoomViewMusicRatationBinding;
|
||||
import com.xscm.moduleutil.widget.floatingView.FloatingMagnetView;
|
||||
|
||||
|
||||
public class MusicRotationView extends FloatingMagnetView {
|
||||
private RoomViewMusicRatationBinding mBinding;
|
||||
|
||||
public MusicRotationView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public MusicRotationView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public MusicRotationView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_view_music_ratation,this,true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启图片旋转动画
|
||||
*/
|
||||
public void startRotateAnimation() {
|
||||
Animation rotateAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.image_rotate);
|
||||
LinearInterpolator lin = new LinearInterpolator();
|
||||
rotateAnimation.setInterpolator(lin);
|
||||
mBinding.rivAvatar.setAnimation(rotateAnimation);
|
||||
mBinding.rivAvatar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
super.setVisibility(visibility);
|
||||
if (visibility == GONE) {
|
||||
// RtcManager.getInstance().setAudioUrl(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭图片旋转动画
|
||||
*/
|
||||
public void endRotateAnimation() {
|
||||
mBinding.rivAvatar.clearAnimation();
|
||||
mBinding.rivAvatar.setAnimation(null);
|
||||
mBinding.rivAvatar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNearestLeft() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
|
||||
import com.petterp.floatingx.assist.helper.FxScopeHelper;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.databinding.RoomDialogMusicWindowOpenBinding;
|
||||
import com.xscm.moduleutil.widget.floatingView.FloatingMagnetView;
|
||||
|
||||
public class MusicView extends FloatingMagnetView {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private int playPattern = 1; //1循环播放 2单曲循环 3随机播放
|
||||
private boolean isPaly = false;
|
||||
// private MusicTable mMusicTable;
|
||||
private OnItemClickListener mOnItemClickListener;
|
||||
// private List<MusicTable> musicTables = new ArrayList<>();
|
||||
private RoomDialogMusicWindowOpenBinding mBinding;
|
||||
|
||||
public MusicView(@NonNull Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public MusicView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public MusicView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
@Override
|
||||
protected boolean isNearestLeft() {
|
||||
return true;
|
||||
}
|
||||
private void initView(Context context) {
|
||||
this.mContext = context;
|
||||
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_dialog_music_window_open,this,true);
|
||||
FxScopeHelper.builder().setLayout(R.layout.room_dialog_music_window_open).build().toControl( this);
|
||||
// playPattern = SpUtils.getPlayPattern();
|
||||
initListener();
|
||||
switch (playPattern) {
|
||||
case 1:
|
||||
// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_circulation);
|
||||
break;
|
||||
case 2:
|
||||
// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_singlecircle);
|
||||
break;
|
||||
case 3:
|
||||
// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_random);
|
||||
break;
|
||||
}
|
||||
// mBinding.seekBar.setProgress(SpUtils.getChannelVolume());
|
||||
mBinding.ivMinx.setOnClickListener(this::onClick);
|
||||
mBinding.ivMusicPlayState.setOnClickListener(this::onClick);
|
||||
mBinding.ivList.setOnClickListener(this::onClick);
|
||||
mBinding.ivNext.setOnClickListener(this::onClick);
|
||||
// mBinding.ivLast.setOnClickListener(this::onClick);
|
||||
// mBinding.ivPattern.setOnClickListener(this::onClick);
|
||||
mBinding.flParent.setOnClickListener(this::onMusicDismiss);
|
||||
mBinding.rlPlayRegion.setOnClickListener(this::onRegion);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mBinding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
|
||||
if (b) {
|
||||
if (progress < 0) {
|
||||
progress = 0;
|
||||
}
|
||||
if (progress > 100) {
|
||||
progress = 100;
|
||||
}
|
||||
if (mOnItemClickListener != null) {
|
||||
mOnItemClickListener.setMusicVolume((int) (progress * 0.6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//icon_music_stop
|
||||
public void setPalyState(boolean b) {
|
||||
this.isPaly = b;
|
||||
if (isPaly) {
|
||||
// mBinding.ivMusicPlayState.setImageResource(R.mipmap.room_music_win_start);
|
||||
} else {
|
||||
mBinding.ivMusicPlayState.setImageResource(R.mipmap.room_music_win_puase);
|
||||
}
|
||||
}
|
||||
|
||||
// public void setData(MusicTable musicTable) {
|
||||
// this.mMusicTable = musicTable;
|
||||
// mBinding.tvMusicTitle.setText(mMusicTable.getTitle());
|
||||
// mBinding.tvSinger.setText(mMusicTable.getAuthor());
|
||||
// }
|
||||
|
||||
|
||||
public void onClick(View view) {
|
||||
int id = view.getId();
|
||||
if (id == R.id.iv_minx) {
|
||||
if (mOnItemClickListener != null) {
|
||||
mOnItemClickListener.minimize();
|
||||
}
|
||||
} else if (id == R.id.iv_list) {
|
||||
if (mOnItemClickListener != null) {
|
||||
mOnItemClickListener.openMusicList();
|
||||
}
|
||||
} else if (id == R.id.iv_music_play_state) {
|
||||
if (mOnItemClickListener != null) {
|
||||
if (isPaly) {
|
||||
mOnItemClickListener.pausePlay();
|
||||
} else {
|
||||
mOnItemClickListener.resumePlay();
|
||||
}
|
||||
}
|
||||
} else if (id == R.id.iv_next) {
|
||||
next();
|
||||
// } else if (id == R.id.iv_last) {
|
||||
// last();
|
||||
}
|
||||
// else if (id == R.id.iv_pattern) {
|
||||
// if (playPattern == 1) {
|
||||
// playPattern = 2;
|
||||
// } else if (playPattern == 2) {
|
||||
// playPattern = 3;
|
||||
// } else if (playPattern == 3) {
|
||||
// playPattern = 1;
|
||||
// }
|
||||
// switch (playPattern) {
|
||||
// case 1:
|
||||
//// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_circulation);
|
||||
// break;
|
||||
// case 2:
|
||||
// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_singlecircle);
|
||||
// break;
|
||||
// case 3:
|
||||
//// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_random);
|
||||
// break;
|
||||
// }
|
||||
//// SpUtils.setPlayPattern(playPattern);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void addOnItemClickListener(OnItemClickListener onItemClickListener) {
|
||||
this.mOnItemClickListener = onItemClickListener;
|
||||
}
|
||||
|
||||
// public void setMusicList(List<MusicTable> lovalMusicData) {
|
||||
// this.musicTables = lovalMusicData;
|
||||
// }
|
||||
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void minimize();
|
||||
|
||||
void openMusicList();
|
||||
|
||||
// void playMusic(MusicTable musicTable);
|
||||
|
||||
void stopPlay();
|
||||
|
||||
void pausePlay();
|
||||
|
||||
void resumePlay();
|
||||
|
||||
void setMusicVolume(int progress);
|
||||
}
|
||||
|
||||
|
||||
// public void initData() {
|
||||
// musicTables = DbController.getInstance(mContext).queryMusicListAll();
|
||||
// }
|
||||
|
||||
public void next() {
|
||||
// initData();
|
||||
// if (musicTables.size() == 0) {
|
||||
// return;
|
||||
// }
|
||||
// musicTables.size();
|
||||
// int index = 0;
|
||||
// if (mMusicTable != null) {
|
||||
// for (int i = 0; i < musicTables.size(); i++) {
|
||||
// if (mMusicTable.getId().equals(musicTables.get(i).getId())) {
|
||||
// index = i;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// //下一首播放逻辑
|
||||
// if (playPattern == 1) {//列表循环
|
||||
// index++;
|
||||
// } else if (playPattern == 3) {//随机播放
|
||||
// if (musicTables.size() > 1) {
|
||||
// Random random = new Random();
|
||||
// index = random.nextInt(musicTables.size() - 1);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (index == musicTables.size()) {
|
||||
// mMusicTable = musicTables.get(0);
|
||||
// } else {
|
||||
// mMusicTable = musicTables.get(index);
|
||||
// }
|
||||
// if (mOnItemClickListener != null) {
|
||||
// mOnItemClickListener.playMusic(mMusicTable);
|
||||
// }
|
||||
// setData(mMusicTable);
|
||||
}
|
||||
|
||||
public void last() {
|
||||
// initData();
|
||||
// int index = 0;
|
||||
// for (int i = 0; i < musicTables.size(); i++) {
|
||||
// if (mMusicTable.getId().equals(musicTables.get(i).getId())) {
|
||||
// index = i;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// //上一首播放逻辑
|
||||
// if (playPattern == 1) {//列表循环
|
||||
// index--;
|
||||
// } else if (playPattern == 3) {//随机播放
|
||||
// Random random = new Random();
|
||||
// index = random.nextInt(musicTables.size() - 1);
|
||||
// }
|
||||
// if (index <= 0) {
|
||||
// mMusicTable = musicTables.get(musicTables.size() - 1);
|
||||
// } else {
|
||||
// mMusicTable = musicTables.get(index);
|
||||
// }
|
||||
// if (mOnItemClickListener != null) {
|
||||
// mOnItemClickListener.playMusic(mMusicTable);
|
||||
// }
|
||||
// setData(mMusicTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮循方式
|
||||
*/
|
||||
public void updateLoopType(int type) {
|
||||
playPattern = type;
|
||||
// if (type == 1) {
|
||||
// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_circulation);
|
||||
// } else if (type == 2) {
|
||||
// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_singlecircle);
|
||||
// } else {
|
||||
// mBinding.ivPattern.setImageResource(R.mipmap.room_music_win_random);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击外部弹窗消失
|
||||
*/
|
||||
public void onMusicDismiss(View view) {
|
||||
if (mOnItemClickListener != null) {
|
||||
mOnItemClickListener.minimize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击播放器内部无效
|
||||
*/
|
||||
|
||||
public void onRegion(View view) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.GridView;
|
||||
|
||||
public class MyGridView extends GridView {
|
||||
public MyGridView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public MyGridView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int heightSpec;
|
||||
|
||||
if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
|
||||
// The great Android "hackatlon", the love, the magic.
|
||||
// The two leftmost bits in the height measure spec have
|
||||
// a special meaning, hence we can't use them to describe height.
|
||||
heightSpec = MeasureSpec.makeMeasureSpec(
|
||||
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
|
||||
}
|
||||
else {
|
||||
// Any other height should be respected as is.
|
||||
heightSpec = heightMeasureSpec;
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightSpec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
public class NewView extends AppCompatImageView {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
|
||||
public NewView(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
|
||||
public NewView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public NewView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
private void initView(Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
public void setNew(int isNew) {
|
||||
if (isNew == 0) {
|
||||
this.setVisibility(GONE);
|
||||
} else if (isNew == 1) {
|
||||
this.setVisibility(VISIBLE);
|
||||
setImageResource(R.mipmap.ic_user_new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
|
||||
public class NobilityView extends AppCompatImageView {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
|
||||
public NobilityView(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public NobilityView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public NobilityView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
private void initView(Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
public void setNobility(String url) {
|
||||
setVisibility(TextUtils.isEmpty(url) ? GONE : VISIBLE);
|
||||
com.xscm.moduleutil.utils.ImageUtils.loadImageView(url, this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,609 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
public class PKProgressBar extends View {
|
||||
// 文字背景颜色
|
||||
private int textBgColor = 0;
|
||||
// 绘制跟随进度条移动的数字的画笔
|
||||
private Paint paintNumber;
|
||||
private Paint bitmapPaint;
|
||||
private Paint paintBar;
|
||||
private Paint paintOtherBar;
|
||||
private Paint paintLight;
|
||||
private Paint leftTextPaintBg;
|
||||
private Paint rightTextPaintBg;
|
||||
private int backGroundColor = Color.GRAY;
|
||||
private int barColor = Color.RED;
|
||||
private Drawable leftDrawable;
|
||||
private Drawable rightDrawable;
|
||||
private Drawable rightDrawableOne;
|
||||
private Drawable rightDrawableTwo;
|
||||
private Drawable rightDrawableThree;
|
||||
private OnProgressChangeListener onProgressChangeListener;
|
||||
private int halfDrawableWidth = 0;
|
||||
private int halfDrawableHeight = 0;
|
||||
private int barPadding = 0;
|
||||
private boolean isRound = true;
|
||||
private float progress = 50f;
|
||||
private float max = 100f;
|
||||
private double progressPercentage = 0.5;
|
||||
private float viewWidth = 0f;
|
||||
private int viewHeight = 0;
|
||||
private int progressWidth = 100;
|
||||
private RectF rectFBG = new RectF();
|
||||
private RectF rectFPB = new RectF();
|
||||
private RectF rectFPBO = new RectF();
|
||||
private Path barRoundPath = new Path();
|
||||
private Path barRoundPathOther = new Path();
|
||||
private Rect rectLeftText = new Rect();
|
||||
private Rect rectRightText = new Rect();
|
||||
private Rect rectLeftFollowText = new Rect();
|
||||
private Rect rectRightFollowText = new Rect();
|
||||
private Paint paintRightText;
|
||||
private Paint paintLeftText;
|
||||
private Paint paintBackGround;
|
||||
// 是否使用颜色渐变器
|
||||
private boolean isGradient = false;
|
||||
// 颜色渐变器
|
||||
private LinearGradient linearGradient;
|
||||
private LinearGradient linearGradientOther;
|
||||
private int gradientStartColor = Color.RED;
|
||||
private int gradientEndColor = Color.YELLOW;
|
||||
private int otherGradientStartColor = Color.RED;
|
||||
private int otherGradientEndColor = Color.YELLOW;
|
||||
private int barStartWidth = 0;
|
||||
// 进度条最大绘制位置与最小绘制位置
|
||||
private int barEndWidth = 0;
|
||||
// 圆角大小
|
||||
private float barRadioSize = 0f;
|
||||
private int textColor = Color.WHITE;
|
||||
private int textSize = 12;
|
||||
private boolean textIsBold = true;
|
||||
// 蓝方
|
||||
private String leftTextStr = "";
|
||||
// 红方
|
||||
private String rightTextStr = "";
|
||||
private String leftTeamName = "";
|
||||
private String leftTeamCount = "";
|
||||
private String rightTeamName = "";
|
||||
private String rightTeamCount = "";
|
||||
private String leftFollowTextStr = "";
|
||||
private String rightFollowTextStr = "";
|
||||
private Bitmap lightBitmap;
|
||||
private int lightDrawableId = 0;
|
||||
|
||||
private float leftTextStrWidth = 0f;
|
||||
private float leftTextStrHeight = 0f;
|
||||
private float leftTextStrMargin = 21;
|
||||
private float commonMargin = 6;
|
||||
private float leftTextStrBg = 57;
|
||||
|
||||
private float rightTextStrWidth = 0f;
|
||||
private float rightTextStrHeight = 0f;
|
||||
private float rightTextStrMargin = 21;
|
||||
|
||||
private float leftFollowWidth = 0f;
|
||||
private float leftFollowHeight = 0f;
|
||||
private float rightFollowWidth = 0f;
|
||||
private float rightFollowHeight = 0f;
|
||||
private float followMargin = 16;
|
||||
|
||||
/**
|
||||
* 左侧队名显示状态
|
||||
*/
|
||||
private boolean leftTeamNameVisible = true;
|
||||
|
||||
/**
|
||||
* 左侧团队人数的显示状态
|
||||
*/
|
||||
private boolean leftTeamCountVisible = true;
|
||||
|
||||
/**
|
||||
* 右侧队名显示状态
|
||||
*/
|
||||
private boolean rightTeamNameVisible = true;
|
||||
|
||||
/**
|
||||
* 右侧团队人数的显示状态
|
||||
*/
|
||||
private boolean rightTeamCountVisible = true;
|
||||
|
||||
/**
|
||||
* 左侧的队名宽度
|
||||
*/
|
||||
private float leftTeamNameWidth = 0f;
|
||||
|
||||
/**
|
||||
* 左侧的团队人数宽度
|
||||
*/
|
||||
private float leftTeamCountWidth = 0f;
|
||||
|
||||
/**
|
||||
* 右侧的队名的宽度
|
||||
*/
|
||||
private float rightTeamNameWidth = 0f;
|
||||
|
||||
/**
|
||||
* 右侧的团队人数宽度
|
||||
*/
|
||||
private float rightTeamCountWidth = 0f;
|
||||
|
||||
/**
|
||||
* 右侧用户头像边距
|
||||
*/
|
||||
private final float rightAvatarMargin = 10;
|
||||
|
||||
/**
|
||||
* 右侧用户头像宽度
|
||||
*/
|
||||
private final float rightAvatarwidth = 58;
|
||||
|
||||
public PKProgressBar(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public PKProgressBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PKProgressBar_pk, 0, 0);
|
||||
backGroundColor = typedArray.getColor(R.styleable.PKProgressBar_pk_backGroundColor, Color.GRAY);
|
||||
barColor = typedArray.getColor(R.styleable.PKProgressBar_pk_barColor, Color.RED);
|
||||
leftDrawable = typedArray.getDrawable(R.styleable.PKProgressBar_pk_leftDrawableBg);
|
||||
rightDrawable = typedArray.getDrawable(R.styleable.PKProgressBar_pk_rightDrawableBg);
|
||||
halfDrawableWidth = typedArray.getDimensionPixelSize(R.styleable.PKProgressBar_pk_halfDrawableWidth, 1);
|
||||
halfDrawableHeight = typedArray.getDimensionPixelSize(R.styleable.PKProgressBar_pk_halfDrawableHeight, 1);
|
||||
barPadding = typedArray.getDimensionPixelSize(R.styleable.PKProgressBar_pk_barPadding, 0);
|
||||
isRound = typedArray.getBoolean(R.styleable.PKProgressBar_pk_isRound, true);
|
||||
max = typedArray.getInt(R.styleable.PKProgressBar_pk_max, 100);
|
||||
lightDrawableId = typedArray.getResourceId(R.styleable.PKProgressBar_pk_lightDrawable, 0);
|
||||
textColor = typedArray.getColor(R.styleable.PKProgressBar_pk_textColor, Color.BLACK);
|
||||
textSize = typedArray.getDimensionPixelSize(R.styleable.PKProgressBar_pk_textSize, 13);
|
||||
textIsBold = typedArray.getBoolean(R.styleable.PKProgressBar_pk_textIsBold, false);
|
||||
isGradient = typedArray.getBoolean(R.styleable.PKProgressBar_pk_isGradient, false);
|
||||
gradientStartColor = typedArray.getColor(R.styleable.PKProgressBar_pk_gradientStartColor, Color.RED);
|
||||
gradientEndColor = typedArray.getColor(R.styleable.PKProgressBar_pk_gradientEndColor, Color.YELLOW);
|
||||
otherGradientStartColor = typedArray.getColor(R.styleable.PKProgressBar_pk_otherGradientStartColor, Color.RED);
|
||||
otherGradientEndColor = typedArray.getColor(R.styleable.PKProgressBar_pk_otherGradientEndColor, Color.YELLOW);
|
||||
typedArray.recycle();
|
||||
init();
|
||||
}
|
||||
|
||||
public PKProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// 底部边框背景
|
||||
paintBackGround = new Paint();
|
||||
paintBackGround.setColor(backGroundColor);
|
||||
paintBackGround.setAntiAlias(true);
|
||||
|
||||
paintLight = new Paint();
|
||||
paintLight.setAntiAlias(true);
|
||||
|
||||
// 左半部分进度条
|
||||
paintBar = new Paint();
|
||||
paintBar.setColor(barColor);
|
||||
paintBar.setAntiAlias(true);
|
||||
|
||||
// 右半部分进度条
|
||||
paintOtherBar = new Paint();
|
||||
paintOtherBar.setColor(barColor);
|
||||
paintOtherBar.setAntiAlias(true);
|
||||
|
||||
// 左半部分文字
|
||||
// Typeface plain = Typeface.createFromAsset(getContext().getAssets(), "Roboto-BlackItalic.ttf");/**/
|
||||
paintLeftText = new Paint();
|
||||
// paintLeftText.setTypeface(plain);
|
||||
paintLeftText.setStyle(Paint.Style.FILL);
|
||||
paintLeftText.setColor(textColor);
|
||||
paintLeftText.setTextSize(textSize);
|
||||
|
||||
leftTextStrWidth = paintLeftText != null ? paintLeftText.measureText(leftTextStr) : 0;
|
||||
leftTextStrHeight = paintLeftText != null ? paintLeftText.descent() - paintLeftText.ascent() : 0;
|
||||
|
||||
// 团队数字
|
||||
// Typeface plainNumber = Typeface.createFromAsset(getContext().getAssets(), "Roboto-BlackItalic.ttf");
|
||||
paintNumber = new Paint();
|
||||
// paintNumber.setTypeface(plainNumber);
|
||||
paintNumber.setStyle(Paint.Style.FILL);
|
||||
paintNumber.setColor(textColor);
|
||||
paintNumber.setTextSize(24);
|
||||
|
||||
// 左半部文字的背景
|
||||
leftTextPaintBg = new Paint();
|
||||
leftTextPaintBg.setStyle(Paint.Style.FILL);
|
||||
leftTextPaintBg.setColor(textBgColor);
|
||||
leftTextPaintBg.setAntiAlias(true);
|
||||
|
||||
// 右半部分文字
|
||||
paintRightText = new Paint();
|
||||
paintRightText.setStyle(Paint.Style.FILL);
|
||||
paintRightText.setColor(textColor);
|
||||
paintRightText.setTextSize(textSize);
|
||||
paintRightText.setFakeBoldText(textIsBold);
|
||||
paintRightText.setAntiAlias(true);
|
||||
paintRightText.setTextSkewX(-0.25f);
|
||||
|
||||
if (lightDrawableId != 0) {
|
||||
lightBitmap = BitmapFactory.decodeResource(getResources(), lightDrawableId);
|
||||
}
|
||||
|
||||
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
// 是否需要渐变器
|
||||
if (isGradient) {
|
||||
linearGradient = new LinearGradient(
|
||||
0f,
|
||||
viewHeight / 2f,
|
||||
progressWidth,
|
||||
viewHeight / 2f,
|
||||
gradientStartColor,
|
||||
gradientEndColor,
|
||||
Shader.TileMode.CLAMP
|
||||
);
|
||||
linearGradientOther = new LinearGradient(
|
||||
0f,
|
||||
viewHeight / 2f,
|
||||
progressWidth,
|
||||
viewHeight / 2f,
|
||||
otherGradientStartColor,
|
||||
otherGradientEndColor,
|
||||
Shader.TileMode.CLAMP
|
||||
);
|
||||
if (paintBar != null) {
|
||||
paintBar.setShader(linearGradient);
|
||||
}
|
||||
if (paintOtherBar != null) {
|
||||
paintOtherBar.setShader(linearGradientOther);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
// 光标水平位置
|
||||
viewWidth = ((progressWidth - 3 * barPadding) * (float) progressPercentage + barPadding);
|
||||
viewWidth = Math.max(viewWidth, leftTextStrBg + leftTextStrMargin + leftFollowWidth);
|
||||
viewWidth = Math.min(
|
||||
viewWidth,
|
||||
progressWidth - commonMargin - barPadding - rightAvatarMargin - rightAvatarwidth - rightFollowWidth - followMargin
|
||||
);
|
||||
viewHeight = getHeight();
|
||||
// 圆角大小,为实际进度条高度一半
|
||||
if (isRound) {
|
||||
barRadioSize = (viewHeight - barPadding * 2) / 2f;
|
||||
}
|
||||
|
||||
// 绘制底部边框
|
||||
drawBackground(canvas);
|
||||
// 绘制左半部分进度条
|
||||
drawLeftBar(canvas);
|
||||
// 绘制右半部分进度条
|
||||
drawRightBar(canvas);
|
||||
// 绘制透明图层
|
||||
drawPicture(canvas);
|
||||
// 绘制横向半透明光柱
|
||||
drawLight(canvas);
|
||||
// 绘制跟随进度条移动的文字
|
||||
drawFollowText(canvas);
|
||||
// 绘制队伍信息
|
||||
drawTeamInfoText(canvas);
|
||||
}
|
||||
|
||||
private void drawFollowText(Canvas canvas) {
|
||||
float leftFollowStart = getBoundaryPosition() - leftFollowWidth - halfDrawableWidth - followMargin;
|
||||
float rightFollowStart = getBoundaryPosition() + rightFollowWidth + halfDrawableWidth + followMargin;
|
||||
|
||||
leftTeamCountVisible = leftFollowStart > leftTextStrBg + leftTeamNameWidth + leftTextStrMargin * 2 + leftTeamCountWidth;
|
||||
leftTeamNameVisible = leftFollowStart > leftTextStrBg + commonMargin + leftTeamNameWidth;
|
||||
rightTeamCountVisible = rightFollowStart < (progressWidth - commonMargin * 2 - barPadding - rightAvatarMargin - rightAvatarwidth - rightTeamNameWidth - rightTeamCountWidth);
|
||||
rightTeamNameVisible = rightFollowStart < (progressWidth - commonMargin - rightTeamNameWidth - barPadding - rightAvatarMargin - rightAvatarwidth);
|
||||
}
|
||||
|
||||
private void drawLight(Canvas canvas) {
|
||||
if (lightBitmap == null) {
|
||||
return;
|
||||
}
|
||||
canvas.save();
|
||||
|
||||
// 将画布坐标系移动到画布中央
|
||||
canvas.translate(0f, barPadding);
|
||||
Rect src = new Rect(barStartWidth, 0, barEndWidth, (viewHeight - barPadding) / 2);
|
||||
canvas.drawBitmap(lightBitmap, src, src, paintLight);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
private void drawBackground(Canvas canvas) {
|
||||
canvas.save();
|
||||
rectFBG.set(0f, 0f, progressWidth, viewHeight);
|
||||
if (isRound) {
|
||||
Path path = new Path();
|
||||
path.addRoundRect(new RectF(0f, 0f, getMeasuredWidth(), getMeasuredHeight()), viewHeight / 2f, viewHeight / 2f, Path.Direction.CW);
|
||||
// 先对canvas进行裁剪
|
||||
canvas.clipPath(path, Region.Op.INTERSECT);
|
||||
} else {
|
||||
if (paintBackGround != null) {
|
||||
canvas.drawRect(rectFBG, paintBackGround);
|
||||
}
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
private void drawLeftBar(Canvas canvas) {
|
||||
float right = getBoundaryPosition();
|
||||
float[] radios = {
|
||||
barRadioSize, barRadioSize, 0f, 0f, 0f, 0f, barRadioSize, barRadioSize
|
||||
};
|
||||
canvas.save();
|
||||
rectFPB.set(barPadding, barPadding, right - halfDrawableWidth, viewHeight - barPadding);
|
||||
barRoundPath.reset();
|
||||
barRoundPath.addRoundRect(rectFPB, radios, Path.Direction.CCW);
|
||||
barRoundPath.close();
|
||||
if (isRound) {
|
||||
if (paintBar != null) {
|
||||
canvas.drawPath(barRoundPath, paintBar);
|
||||
}
|
||||
} else {
|
||||
if (paintBar != null) {
|
||||
canvas.drawRect(rectFPB, paintBar);
|
||||
}
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
private void drawRightBar(Canvas canvas) {
|
||||
float left = getBoundaryPosition();
|
||||
float[] radios = {
|
||||
0f, 0f,
|
||||
barRadioSize, barRadioSize,
|
||||
barRadioSize, barRadioSize, 0f, 0f
|
||||
};
|
||||
canvas.save();
|
||||
rectFPBO.set(left + halfDrawableWidth, barPadding, barEndWidth, viewHeight - barPadding);
|
||||
barRoundPathOther.reset();
|
||||
barRoundPathOther.addRoundRect(rectFPBO, radios, Path.Direction.CCW);
|
||||
barRoundPathOther.close();
|
||||
if (isRound) {
|
||||
if (paintOtherBar != null) {
|
||||
canvas.drawPath(barRoundPathOther, paintOtherBar);
|
||||
}
|
||||
} else {
|
||||
if (paintOtherBar != null) {
|
||||
canvas.drawRect(rectFPBO, paintOtherBar);
|
||||
}
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
private void drawTeamInfoText(Canvas canvas) {
|
||||
// 左侧
|
||||
// 绘制文字的背景
|
||||
if (leftTextPaintBg != null) {
|
||||
leftTextPaintBg.setColor(textBgColor);
|
||||
canvas.drawRoundRect(13, 13, 57, viewHeight - 13, viewHeight / 2f, viewHeight / 2f, leftTextPaintBg);
|
||||
}
|
||||
if (paintLeftText != null) {
|
||||
canvas.drawText(leftTextStr, leftTextStrMargin, viewHeight / 2f + leftTextStrHeight / 3f, paintLeftText);
|
||||
}
|
||||
if (leftTeamNameVisible && paintLeftText != null) {
|
||||
// 队名
|
||||
canvas.drawText(leftTeamName, leftTextStrBg + commonMargin, viewHeight / 2f + leftTextStrHeight / 3f, paintLeftText);
|
||||
}
|
||||
if (leftTeamCountVisible && paintLeftText != null) {
|
||||
// 队伍人数
|
||||
// canvas.drawText(leftTeamCount, leftTextStrBg + leftTeamNameWidth + commonMargin * 2, viewHeight / 2f + leftTextStrHeight / 3f, paintLeftText);
|
||||
}
|
||||
// 右侧
|
||||
if (rightTeamNameVisible && paintRightText != null) {
|
||||
// 右侧队名
|
||||
canvas.drawText(rightTeamName, progressWidth - commonMargin - rightTeamNameWidth - barPadding - rightAvatarMargin - rightAvatarwidth, viewHeight / 2f + leftTextStrHeight / 3f, paintRightText);
|
||||
}
|
||||
if (rightTeamCountVisible && paintRightText != null) {
|
||||
// 右侧团队人数
|
||||
// canvas.drawText(rightTeamCount, progressWidth - commonMargin * 2 - barPadding - rightAvatarMargin - rightAvatarwidth - rightTeamNameWidth - rightTeamCountWidth, viewHeight / 2f + leftTextStrHeight / 3f, paintRightText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取极限位置进度条的绘制位置
|
||||
* 1. 最左边
|
||||
* 2. 最左边到圆角区段位置
|
||||
* 3. 中间正常位置
|
||||
* 4. 最右边圆角区段位置
|
||||
* 5. 最右边
|
||||
*
|
||||
* @return 极限位置进度条的绘制位置
|
||||
*/
|
||||
private float getBoundaryPosition() {
|
||||
// 默认为计算的比例位置
|
||||
float boundaryPos = viewWidth;
|
||||
if (progressPercentage == 0.0 || viewWidth == barStartWidth) {
|
||||
// 光标位于最左边
|
||||
boundaryPos = barPadding + leftTextStrBg + leftTextStrMargin + leftFollowWidth;
|
||||
} else if (progressPercentage == 1.0 || viewWidth == barEndWidth) {
|
||||
// 光标位于最右边
|
||||
boundaryPos = barEndWidth - rightAvatarwidth - rightAvatarMargin;
|
||||
} else if ((viewWidth - barStartWidth < barRadioSize || viewWidth - barStartWidth == barRadioSize)
|
||||
&& viewWidth > barStartWidth) {
|
||||
boundaryPos = Math.max(viewWidth, barRadioSize + barStartWidth);
|
||||
} else if ((viewWidth > barEndWidth - barRadioSize || viewWidth == barEndWidth - barRadioSize)
|
||||
&& viewWidth < barEndWidth) {
|
||||
// 光标位于进度条右侧弧形区域
|
||||
boundaryPos = Math.min(viewWidth, barEndWidth - barRadioSize);
|
||||
}
|
||||
return boundaryPos;
|
||||
}
|
||||
|
||||
private void drawPicture(Canvas canvas) {
|
||||
if (leftDrawable != null) {
|
||||
leftDrawable.setBounds(0, 0, 180, viewHeight);
|
||||
leftDrawable.draw(canvas);
|
||||
}
|
||||
if (rightDrawable != null) {
|
||||
rightDrawable.setBounds(
|
||||
(int) rectFPBO.right - 180,
|
||||
(int) rectFPBO.top,
|
||||
(int) rectFPBO.right,
|
||||
(int) rectFPBO.bottom
|
||||
);
|
||||
rightDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||
width = halfDrawableWidth * 2;
|
||||
}
|
||||
if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||
height = halfDrawableHeight * 2;
|
||||
}
|
||||
|
||||
// 记录进度条总长度
|
||||
progressWidth = width;
|
||||
// 记录进度条起始位置
|
||||
barStartWidth = barPadding;
|
||||
// 记录进度条结束位置
|
||||
barEndWidth = progressWidth - barPadding;
|
||||
setMeasuredDimension(width, height);
|
||||
|
||||
leftTextStrWidth = paintRightText != null ? paintRightText.measureText(leftTextStr) : 0;
|
||||
leftTextStrHeight = paintLeftText != null ? paintLeftText.descent() - paintLeftText.ascent() : 0;
|
||||
|
||||
rightTextStrWidth = paintRightText != null ? paintRightText.measureText(rightTextStr) : 0;
|
||||
rightTextStrHeight = paintRightText != null ? paintRightText.descent() - paintRightText.ascent() : 0;
|
||||
|
||||
leftFollowWidth = paintNumber != null ? paintNumber.measureText(leftFollowTextStr) : 0;
|
||||
leftFollowHeight = paintNumber != null ? paintNumber.descent() - paintNumber.ascent() : 0;
|
||||
|
||||
rightFollowWidth = paintNumber != null ? paintNumber.measureText(rightFollowTextStr) : 0;
|
||||
rightFollowHeight = paintNumber != null ? paintNumber.descent() - paintNumber.ascent() : 0;
|
||||
|
||||
rightTeamNameWidth = paintLeftText != null ? paintLeftText.measureText(rightTeamName) : 0;
|
||||
rightTeamCountWidth = paintLeftText != null ? paintLeftText.measureText(rightTeamCount) : 0;
|
||||
|
||||
leftTeamNameWidth = paintLeftText != null ? paintLeftText.measureText(leftTeamName) : 0;
|
||||
leftTeamCountWidth = paintLeftText != null ? paintLeftText.measureText(leftTeamCount) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进度变化监听器接口
|
||||
*/
|
||||
public interface OnProgressChangeListener {
|
||||
/**
|
||||
* 当进度发生变化时触发
|
||||
*
|
||||
* @param progress 当前进度值
|
||||
*/
|
||||
void onOnProgressChange(int progress);
|
||||
|
||||
/**
|
||||
* 当进度完成时触发
|
||||
*/
|
||||
void onOnProgressFinish();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据左右值设置动画进度
|
||||
*
|
||||
* @param leftValue 左值
|
||||
* @param rightValue 右值
|
||||
*/
|
||||
public synchronized void setAnimProgress(long leftValue, long rightValue) {
|
||||
leftFollowTextStr = String.valueOf(leftValue);
|
||||
rightFollowTextStr = String.valueOf(rightValue);
|
||||
if (leftValue + rightValue == 0L) {
|
||||
setAnimProgress(50f);
|
||||
} else {
|
||||
setAnimProgress(100f * leftValue / (leftValue + rightValue));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入百分比值设置动画进度
|
||||
*
|
||||
* @param progressValue 百分比值
|
||||
*/
|
||||
public void setAnimProgress(float progressValue) {
|
||||
if (progressValue < 0 || progressValue > 100) {
|
||||
return;
|
||||
}
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(progress, progressValue);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
setProgress(Float.parseFloat(valueAnimator.getAnimatedValue().toString()));
|
||||
}
|
||||
});
|
||||
Interpolator interpolator = new AccelerateDecelerateInterpolator();
|
||||
animator.setInterpolator(interpolator);
|
||||
animator.setDuration((long) (Math.abs(progress - progressValue) * 20));
|
||||
animator.start();
|
||||
}
|
||||
|
||||
public void setProgress(float progressValue) {
|
||||
if (progress <= max) {
|
||||
progress = progressValue;
|
||||
} else if (progress < 0) {
|
||||
progress = 0f;
|
||||
} else {
|
||||
progress = max;
|
||||
}
|
||||
progressPercentage = progress / max;
|
||||
doProgressRefresh();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private synchronized void doProgressRefresh() {
|
||||
if (onProgressChangeListener != null) {
|
||||
onProgressChangeListener.onOnProgressChange((int) progress);
|
||||
if (progress >= max) {
|
||||
onProgressChangeListener.onOnProgressFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置颜色渐变器
|
||||
public void setLinearGradient(LinearGradient linearGradient) {
|
||||
this.linearGradient = linearGradient;
|
||||
}
|
||||
|
||||
// 设置进度监听器
|
||||
public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {
|
||||
this.onProgressChangeListener = onProgressChangeListener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PayResult {
|
||||
private String resultStatus;
|
||||
private String result;
|
||||
private String memo;
|
||||
|
||||
public PayResult(Map<String, String> rawResult) {
|
||||
if (rawResult == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String key : rawResult.keySet()) {
|
||||
if (TextUtils.equals(key, "resultStatus")) {
|
||||
resultStatus = rawResult.get(key);
|
||||
} else if (TextUtils.equals(key, "result")) {
|
||||
result = rawResult.get(key);
|
||||
} else if (TextUtils.equals(key, "memo")) {
|
||||
memo = rawResult.get(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "resultStatus={" + resultStatus + "};memo={" + memo
|
||||
+ "};result={" + result + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the resultStatus
|
||||
*/
|
||||
public String getResultStatus() {
|
||||
return resultStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the memo
|
||||
*/
|
||||
public String getMemo() {
|
||||
return memo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the result
|
||||
*/
|
||||
public String getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import static com.xscm.moduleutil.utils.permission.PermissionUtils.TAG;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alipay.sdk.app.PayTask;
|
||||
import com.blankj.utilcode.util.ToastUtils;
|
||||
import com.orhanobut.logger.Logger;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.AppPay;
|
||||
import com.xscm.moduleutil.event.PayEvent;
|
||||
import com.tencent.liteav.base.Log;
|
||||
import com.tencent.mm.opensdk.constants.ConstantsAPI;
|
||||
import com.tencent.mm.opensdk.modelbase.BaseResp;
|
||||
import com.tencent.mm.opensdk.modelpay.PayReq;
|
||||
import com.tencent.mm.opensdk.openapi.IWXAPI;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PaymentUtil {
|
||||
private static final int SDK_PAY_FLAG = 1;
|
||||
public static void payAlipay(final Context mContext, String orderNo) {
|
||||
// 订单信息
|
||||
final String orderInfo = orderNo;
|
||||
Runnable payRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PayTask alipay = new PayTask((Activity) mContext);
|
||||
Map<String, String> result = alipay.payV2(orderInfo, true);
|
||||
|
||||
Message msg = new Message();
|
||||
msg.what = SDK_PAY_FLAG;
|
||||
msg.obj = result;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
};
|
||||
// 必须异步调用
|
||||
Thread payThread = new Thread(payRunnable);
|
||||
payThread.start();
|
||||
}
|
||||
|
||||
|
||||
public static void payWxMiniProgram2(IWXAPI api, AppPay appPay) {
|
||||
if (!api.isWXAppInstalled()) {
|
||||
ToastUtils.showShort("请安装微信");
|
||||
return;
|
||||
}
|
||||
PayReq request = new PayReq();
|
||||
request.appId = appPay.getWx().getAppid();
|
||||
request.partnerId = appPay.getWx().getPartnerid();
|
||||
request.prepayId= appPay.getWx().getPrepayid();
|
||||
request.packageValue = "Sign=WXPay";
|
||||
request.nonceStr= appPay.getWx().getNoncestr();
|
||||
request.timeStamp= appPay.getWx().getTimestamp();
|
||||
request.sign= appPay.getWx().getSign();
|
||||
api.sendReq(request);
|
||||
|
||||
Logger.e("@@@@@","payWxMiniProgram2:"+request);
|
||||
|
||||
}
|
||||
|
||||
public void onResp(BaseResp resp){
|
||||
if(resp.getType()== ConstantsAPI.COMMAND_PAY_BY_WX){
|
||||
Log.d(TAG,"onPayFinish,errCode="+resp.errCode);
|
||||
// AlertDialog.Builder builder=new AlertDialog.Builder(this);
|
||||
// builder.setTitle("");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
public static Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case SDK_PAY_FLAG: {
|
||||
PayResult payResult = new PayResult((Map<String, String>) msg.obj);
|
||||
/**
|
||||
对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。
|
||||
*/
|
||||
// 同步返回需要验证的信息
|
||||
String resultInfo = payResult.getResult();
|
||||
String resultStatus = payResult.getResultStatus();
|
||||
// 判断resultStatus 为9000则代表支付成功
|
||||
if (TextUtils.equals(resultStatus, "9000")) {
|
||||
// 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
|
||||
ToastUtils.showShort("支付成功");
|
||||
PayEvent messageEvent = new PayEvent(Constants.PAYSUCESS, "支付成功");
|
||||
EventBus.getDefault().post(messageEvent);
|
||||
} else if (TextUtils.equals(resultStatus, "6001")) {
|
||||
// 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
|
||||
ToastUtils.showShort("支付取消");
|
||||
PayEvent messageEvent = new PayEvent(-2, "支付取消");
|
||||
EventBus.getDefault().post(messageEvent);
|
||||
} else {
|
||||
// 该笔订单真实的支付结果,需要依赖服务端的异步通知。
|
||||
ToastUtils.showShort("支付失败");
|
||||
// StatisticsUtils.addEvent(MyApplication.getInstance(), Constant.OpenInstall.ALIRECHARGEFAIL);
|
||||
// StatisticsUtils.addEvent(MyApplication.getInstance(), Constant.OpenInstall.RECHARGEFAIL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import static com.liulishuo.okdownload.OkDownloadProvider.context;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.blankj.utilcode.util.LogUtils;
|
||||
import com.petterp.floatingx.FloatingX;
|
||||
import com.petterp.floatingx.assist.FxGravity;
|
||||
import com.petterp.floatingx.assist.helper.FxAppHelper;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.event.MqttBean;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* @Author
|
||||
* @Time 2025/7/18 21:52
|
||||
* @Description 飘屏管理器
|
||||
*/
|
||||
public class PiaoPingManager {
|
||||
private static PiaoPingManager instance;
|
||||
private WindowManager windowManager;
|
||||
private View piaoPingView;
|
||||
private boolean isPiaoPingShown = false;
|
||||
private Queue<MqttBean> messageQueue = new ConcurrentLinkedQueue<>(); // 消息队列
|
||||
private boolean isAnimating = false; // 动画状态标记
|
||||
private PiaoPingManager(Context context) {
|
||||
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
if (windowManager == null) return;
|
||||
|
||||
// 创建飘屏消息的布局
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
piaoPingView = inflater.inflate(R.layout.item_piaoping, null);
|
||||
}
|
||||
|
||||
public static synchronized PiaoPingManager getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
instance = new PiaoPingManager(context.getApplicationContext());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void showPiaoPingMessage(MqttBean mqttBean) {
|
||||
// 创建 FloatingX 配置
|
||||
// 添加到队列
|
||||
messageQueue.offer(mqttBean);
|
||||
// 如果当前没有动画正在进行,则开始处理
|
||||
if (!isAnimating) {
|
||||
processNextMessage();
|
||||
}
|
||||
|
||||
}
|
||||
private void processNextMessage() {
|
||||
if (messageQueue.isEmpty()) {
|
||||
isAnimating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
isAnimating = true;
|
||||
MqttBean mqttBean = messageQueue.poll();
|
||||
displayMessage(mqttBean);
|
||||
}
|
||||
|
||||
private void displayMessage(MqttBean mqttBean) {
|
||||
// 获取悬浮窗视图并设置内容
|
||||
View floatingView = LayoutInflater.from(context).inflate(R.layout.item_piaoping, new FrameLayout(context), false);
|
||||
TextView textView = floatingView.findViewById(R.id.tv_name);
|
||||
TextView textView2 = floatingView.findViewById(R.id.tv_to_name);
|
||||
textView2.setText("送给" + mqttBean.getList().getToUserName());
|
||||
textView.setText(mqttBean.getList().getFromUserName());
|
||||
ImageUtils.loadHeadCC(mqttBean.getList().getGift_picture(), floatingView.findViewById(R.id.iv_piaoping));
|
||||
TextView tv_time = floatingView.findViewById(R.id.tv_num);
|
||||
tv_time.setText("x" + mqttBean.getList().getNumber());
|
||||
|
||||
// 先将视图放置在屏幕右侧外部,避免闪现问题
|
||||
floatingView.setTranslationX(10000); // 先放到屏幕外
|
||||
|
||||
FxAppHelper fxAppHelper = FxAppHelper.builder()
|
||||
.setContext(context)
|
||||
.setLayoutView(floatingView)
|
||||
.setGravity(FxGravity.RIGHT_OR_TOP)
|
||||
.setX(0)
|
||||
.setY(100)
|
||||
.build();
|
||||
|
||||
FloatingX.install(fxAppHelper).show();
|
||||
|
||||
// 首先从右侧滑入到屏幕中央
|
||||
floatingView.post(() -> {
|
||||
// 确保初始位置在屏幕右侧外部
|
||||
floatingView.setTranslationX(floatingView.getWidth());
|
||||
|
||||
// 第一阶段:从右到屏幕右侧边缘(缓慢进入)
|
||||
ObjectAnimator animator1 = ObjectAnimator.ofFloat(floatingView, "translationX",
|
||||
floatingView.getWidth(), 0f);
|
||||
animator1.setDuration(1500); // 延长动画时间到1.5秒
|
||||
animator1.setInterpolator(new DecelerateInterpolator(2.0f)); // 更平缓的减速效果
|
||||
animator1.start();
|
||||
|
||||
// 第二阶段:延迟1秒后从当前位置向左滑出
|
||||
floatingView.postDelayed(() -> {
|
||||
ObjectAnimator animator2 = ObjectAnimator.ofFloat(floatingView, "translationX",
|
||||
0f, -floatingView.getWidth());
|
||||
animator2.setDuration(1500); // 延长动画时间到1.5秒
|
||||
animator2.setInterpolator(new DecelerateInterpolator(2.0f)); // 更平缓的减速效果
|
||||
animator2.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// 动画结束后移除悬浮窗
|
||||
FloatingX.uninstallAll();
|
||||
// 处理下一个消息
|
||||
processNextMessage();
|
||||
}
|
||||
});
|
||||
animator2.start();
|
||||
}, 1000); // 停留1秒
|
||||
});
|
||||
}
|
||||
|
||||
public void subscribe() {
|
||||
try {
|
||||
if (!EventBus.getDefault().isRegistered(this)) {
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 处理重复注册或其他异常
|
||||
LogUtils.e("PiaoPingManager subscribe error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void unsubscribe() {
|
||||
try {
|
||||
if (EventBus.getDefault().isRegistered(this)) {
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 处理取消注册异常
|
||||
LogUtils.e("PiaoPingManager unsubscribe error: " + e.getMessage());
|
||||
}
|
||||
messageQueue.clear();
|
||||
isAnimating = false; // 重置动画状态
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onMessageReceived(MqttBean mqttBean) {
|
||||
showPiaoPingMessage(mqttBean);
|
||||
|
||||
// FxAppHelper fxAppHelper = FxAppHelper.builder().setContext( context).setLayout(R.layout.item_piaoping).build();
|
||||
// FloatingX.install(fxAppHelper).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class RankRecycleView extends RecyclerView {
|
||||
private float mDownPosX = 0;
|
||||
private float mDownPosY = 0;
|
||||
public RankRecycleView(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public RankRecycleView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public RankRecycleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
requestDisallowInterceptTouchEvent(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
final float x = ev.getX();
|
||||
final float y = ev.getY();
|
||||
|
||||
final int action = ev.getAction();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mDownPosX = x;
|
||||
mDownPosY = y;
|
||||
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float deltaX = Math.abs(x - mDownPosX);
|
||||
final float deltaY = Math.abs(y - mDownPosY);
|
||||
//拦截左右滑动
|
||||
if (deltaX > deltaY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
final float x = ev.getX();
|
||||
final float y = ev.getY();
|
||||
|
||||
final int action = ev.getAction();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mDownPosX = x;
|
||||
mDownPosY = y;
|
||||
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float deltaX = Math.abs(x - mDownPosX);
|
||||
final float deltaY = Math.abs(y - mDownPosY);
|
||||
//拦截左右滑动
|
||||
if (deltaX > deltaY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
@SuppressLint("AppCompatCustomView")
|
||||
public class RoleView extends ImageView {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public RoleView(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
|
||||
public RoleView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public RoleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
|
||||
private void initView(Context context) {
|
||||
this.mContext = context;
|
||||
// setVisibility(GONE);
|
||||
// setImageResource(R.mipmap.img_host);
|
||||
}
|
||||
|
||||
|
||||
public void setRole(int role) {
|
||||
this.setVisibility(VISIBLE);
|
||||
if (role == 1) {
|
||||
setImageResource(R.mipmap.img_host);
|
||||
} else if (role == 2) {
|
||||
setImageResource(R.mipmap.img_admin);
|
||||
} else if (role == 5) {
|
||||
setImageResource(R.mipmap.img_official);
|
||||
} else {
|
||||
this.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.UserInfo;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
|
||||
|
||||
/**
|
||||
* 项目名称 qipao-android
|
||||
* 包名:com.yutang.xqipao.utils.view
|
||||
* 创建人 王欧
|
||||
* 创建时间 2020/4/9 12:46 PM
|
||||
* 描述 describe
|
||||
*/
|
||||
public class RoomDefaultWheatView extends BaseWheatView {
|
||||
public ImageView mIvTagBoss;
|
||||
public TextView mTvTime;
|
||||
public TextView tv_time_pk;
|
||||
|
||||
private boolean showBoss;//显示老板标识
|
||||
|
||||
public RoomDefaultWheatView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public RoomDefaultWheatView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RoomDefaultWheatView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initPit(Context context, AttributeSet attrs) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoomDefaultWheatView);
|
||||
pitNumber = typedArray.getString(R.styleable.RoomDefaultWheatView_room_wheat_number);
|
||||
typedArray.recycle();
|
||||
mIvTagBoss = findViewById(R.id.iv_tag_boos);
|
||||
mTvTime = findViewById(R.id.tv_time);
|
||||
tv_time_pk = findViewById(R.id.tv_time_pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.room_view_default_wheat;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setPitData(RoomPitBean bean) {
|
||||
sex = bean.getSex();
|
||||
if (isOn()) {
|
||||
//开启声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
mTvName.setText(bean.getNickname());
|
||||
ImageUtils.loadHeadCC(bean.getAvatar(), mRiv);
|
||||
if (TextUtils.isEmpty(pitBean.getDress())) {
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mIvFrame.setVisibility(VISIBLE);
|
||||
mIvFrame.setSource(pitBean.getDress(), 1);
|
||||
// ImageUtils.loadDecorationAvatar(pitBean.getDress_picture(), mIvFrame);
|
||||
}
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(GONE);
|
||||
}
|
||||
} else {
|
||||
mTvName.setText(
|
||||
"-1".equals(pitNumber) ? "" :
|
||||
"9".equals(pitNumber) ? "主持位" :
|
||||
"10".equals(pitNumber) ? "嘉宾位" :
|
||||
pitNumber + "号麦位"
|
||||
);
|
||||
//麦位上锁
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(VISIBLE);
|
||||
ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default, mRiv);
|
||||
} else {
|
||||
// mIvTagBoss.setVisibility(GONE);
|
||||
// @DrawableRes int origin = getOriginImage();
|
||||
// ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo :
|
||||
// (origin == 0 ? R.mipmap.room_ic_wheat_default : origin), mRiv);
|
||||
mRiv.setImageResource(bean.getIs_lock() == 1 ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default);
|
||||
// ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default, mRiv);
|
||||
}
|
||||
if (isMute()) {
|
||||
ImageUtils.loadRes(R.mipmap.room_microphone_off, mIvSex);
|
||||
}
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
mIvFace.remove();
|
||||
//停止声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(GONE);
|
||||
}
|
||||
if (showSexIcon) {
|
||||
checkSex();
|
||||
}
|
||||
if (pitBean.getNickname() == null || pitBean.getNickname().isEmpty()) {
|
||||
mCharmView.setVisibility(GONE);
|
||||
} else {
|
||||
mCharmView.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
if (pitBean.is_pk() ){
|
||||
if (pitBean.getUser_id()!=null && !pitBean.getUser_id().equals("0") && !pitBean.getUser_id().isEmpty()) {
|
||||
tv_time_pk.setVisibility(VISIBLE);
|
||||
setSex(pitBean.getCharm(),false);
|
||||
mCharmView.setVisibility(GONE);
|
||||
}else {
|
||||
tv_time_pk.setVisibility(GONE);
|
||||
}
|
||||
// ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mCharmView.getLayoutParams();
|
||||
// params.width = 35;
|
||||
// mCharmView.setLayoutParams(params);
|
||||
|
||||
}else {
|
||||
tv_time_pk.setVisibility(GONE);
|
||||
mCharmView.setVisibility(VISIBLE);
|
||||
// ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mCharmView.getLayoutParams();
|
||||
// params.width = 52;
|
||||
// mCharmView.setLayoutParams(params);
|
||||
}
|
||||
|
||||
// setCardiac(pitBean.getPit_number(), 0.0f);
|
||||
}
|
||||
public void setSex( String value, boolean format) {
|
||||
if (format) {
|
||||
tv_time_pk.setText(value);
|
||||
} else {
|
||||
try {
|
||||
long xd = Long.parseLong(value);
|
||||
if (xd > 9999 || xd < -9999) {
|
||||
tv_time_pk.setText(String.format("%.2fw", xd / 10000.0f));
|
||||
// mBinding.tvValue.setText(String.valueOf(xd));
|
||||
} else {
|
||||
tv_time_pk.setText(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean showSexIcon = false;
|
||||
private String sex;
|
||||
|
||||
public boolean isMale() {
|
||||
return "1".equals(sex);
|
||||
}
|
||||
|
||||
public boolean isFemale() {
|
||||
return "2".equals(sex);
|
||||
}
|
||||
|
||||
public void setShowSexIcon(boolean show) {
|
||||
showSexIcon = show;
|
||||
}
|
||||
|
||||
public void checkSex() {
|
||||
if (isOn()) {
|
||||
mIvSex.setVisibility(VISIBLE);
|
||||
if (!TextUtils.isEmpty(sex)) {
|
||||
if (UserInfo.MALE.equals(sex)) {
|
||||
mIvSex.setBackgroundResource(R.drawable.room_xq_wheat_male_mask);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(R.mipmap.ic_room_xq_wno_male);
|
||||
} else {
|
||||
mIvSex.setBackgroundResource(R.drawable.room_xq_wheat_female_mask);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(R.mipmap.ic_room_xq_wno_female);
|
||||
}
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示老板标识
|
||||
*/
|
||||
public void setIsBossShow(String is_boss_pit) {
|
||||
showBoss = "1".equals(is_boss_pit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启计时
|
||||
*/
|
||||
public void setTime(int time) {
|
||||
if (time == 0) {
|
||||
mTvTime.setText("");
|
||||
mTvTime.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mTvTime.setText(String.format("%s'%s", time / 60, time % 60));
|
||||
mTvTime.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMaoziIcon() {
|
||||
View maozi = findViewById(R.id.iv_maozi);
|
||||
if (maozi != null) maozi.setVisibility(GONE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRemoteSoundLevelUpdate(String userId, int soundLevel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalSoundLevelUpdate(int volume) {
|
||||
if (volume==0){
|
||||
mIvRipple.stopAnimation();
|
||||
} else {
|
||||
if (pitBean.getUser_id().equals(SpUtil.getUserId()) && closePhone) {
|
||||
mIvRipple.stopAnimation();
|
||||
}else {
|
||||
mIvRipple.post(() -> {
|
||||
if (!mIvRipple.isAnimating()) {
|
||||
mIvRipple.startAnimation();
|
||||
}
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userJoined(int userId, int elapsd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userOffline(int userId, int reason) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.ConstraintSet;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.UserOnlineStatusBean;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
import com.xscm.moduleutil.color.ThemeableDrawableUtils;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
public class RoomFriendshipWheatView extends BaseWheatView {
|
||||
|
||||
public ImageView mIvTagBoss;
|
||||
public TextView mTvTime;
|
||||
public ImageView iv_on_line;
|
||||
private ImageView iv_tag_type;
|
||||
public WheatCharmView mCharmView;
|
||||
public TextView tv_zhul;
|
||||
|
||||
private boolean showBoss;//显示老板标识
|
||||
private boolean showSexIcon = false;
|
||||
private String sex;
|
||||
|
||||
public RoomFriendshipWheatView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public RoomFriendshipWheatView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public RoomFriendshipWheatView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initPit(Context context, AttributeSet attrs) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoomMakeWheatView);
|
||||
pitNumber = typedArray.getString(R.styleable.RoomMakeWheatView_room_make_wheat_number);
|
||||
pitImageVId = typedArray.getResourceId(R.styleable.RoomMakeWheatView_room_make_pic, 0);
|
||||
typedArray.recycle();
|
||||
mIvTagBoss = findViewById(R.id.iv_tag_boos);
|
||||
mTvTime = findViewById(R.id.tv_time);
|
||||
iv_on_line = findViewById(R.id.iv_online);
|
||||
iv_tag_type = findViewById(R.id.iv_tag_type);
|
||||
mCharmView = findViewById(R.id.charm_view);
|
||||
mRiv.setImageResource(pitImageVId);
|
||||
tv_zhul = findViewById(R.id.tv_zhul);
|
||||
|
||||
// 为 tv_zhul 设置点击监听
|
||||
if (tv_zhul != null) {
|
||||
tv_zhul.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mOnZhulClickListener != null && pitBean != null) {
|
||||
mOnZhulClickListener.onZhulClick(RoomFriendshipWheatView.this, pitBean);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.room_view_friendship_wheat;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setPitData(RoomPitBean bean) {
|
||||
sex = bean.getSex();
|
||||
if (isOn()) {
|
||||
//开启声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
mTvName.setText(bean.getNickname());
|
||||
ImageUtils.loadCenterCrop(bean.getAvatar(), mRiv);
|
||||
if (TextUtils.isEmpty(pitBean.getDress_picture())) {
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mIvFrame.setVisibility(VISIBLE);
|
||||
}
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(GONE);
|
||||
}
|
||||
} else {
|
||||
mTvName.setText((!"10".equals(pitBean.getPit_number()) && !"9".equals(pitBean.getPit_number())) ? pitBean.getPit_number() : "");
|
||||
//麦位上锁
|
||||
//麦位上锁
|
||||
// if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
// mIvTagBoss.setVisibility(VISIBLE);
|
||||
// ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.jiaoy, mRiv);
|
||||
// } else {
|
||||
// mRiv.setImageResource(bean.getIs_lock() == 1 ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.jiaoy);
|
||||
// }
|
||||
if (pitBean.getPit_number().equals("4") || pitBean.getPit_number().equals("5") || pitBean.getPit_number().equals("6")) {
|
||||
ImageUtils.loadRes(R.mipmap.jiaoy_n, mRiv);
|
||||
} else {
|
||||
ImageUtils.loadRes(R.mipmap.jiaoy, mRiv);
|
||||
}
|
||||
if (isMute()) {
|
||||
ImageUtils.loadRes(R.mipmap.room_microphone_off, mIvSex);
|
||||
}
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
mIvFace.remove();
|
||||
//停止声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(GONE);
|
||||
}
|
||||
if (showSexIcon) {
|
||||
checkSex();
|
||||
}
|
||||
if (pitBean.getPit_number().equals("9")) {
|
||||
iv_tag_type.setVisibility(VISIBLE);
|
||||
iv_tag_type.setImageResource(R.mipmap.zc);
|
||||
switchCharmViewPosition(false);
|
||||
tv_zhul.setVisibility(GONE);
|
||||
} else if (pitBean.getPit_number().equals("10")) {
|
||||
iv_tag_type.setVisibility(VISIBLE);
|
||||
iv_tag_type.setImageResource(R.mipmap.jb);
|
||||
switchCharmViewPosition(false);
|
||||
tv_zhul.setVisibility(GONE);
|
||||
} else {
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
switchCharmViewPosition(true);
|
||||
// tv_zhul.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
if (pitBean.getPit_number().equals("1") || pitBean.getPit_number().equals("2") || pitBean.getPit_number().equals("3")) {
|
||||
ThemeableDrawableUtils.setThemeableRoundedBackground(tv_zhul, Color.parseColor("#D449E4"), 53);
|
||||
tv_zhul.setTextColor(Color.parseColor("#FFFFFF"));
|
||||
} else if (pitBean.getPit_number().equals("4") || pitBean.getPit_number().equals("5") || pitBean.getPit_number().equals("6")) {
|
||||
ThemeableDrawableUtils.setThemeableRoundedBackground(tv_zhul, Color.parseColor("#4965E4"), 53);
|
||||
tv_zhul.setTextColor(Color.parseColor("#FFFFFF"));
|
||||
}
|
||||
}
|
||||
|
||||
public void checkSex() {
|
||||
if (isOn()) {
|
||||
mIvSex.setVisibility(VISIBLE);
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换 charm_view 的显示位置
|
||||
*
|
||||
* @param showAtTagTypePosition true-显示在 iv_tag_type 位置,false-显示在原始位置
|
||||
*/
|
||||
|
||||
public void switchCharmViewPosition(boolean showAtTagTypePosition) {
|
||||
if (mCharmView == null) return;
|
||||
|
||||
// 确保视图已经加载完成
|
||||
if (getWidth() == 0 || getHeight() == 0) {
|
||||
post(() -> switchCharmViewPosition(showAtTagTypePosition));
|
||||
return;
|
||||
}
|
||||
|
||||
ConstraintSet constraintSet = new ConstraintSet();
|
||||
ConstraintLayout constraintLayout = (ConstraintLayout) getChildAt(0);
|
||||
constraintSet.clone(constraintLayout);
|
||||
|
||||
if (showAtTagTypePosition) {
|
||||
// 显示在 tag_type 位置(替代 tag_type 的位置)
|
||||
if (iv_tag_type != null) {
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
}
|
||||
|
||||
// 清除原有约束
|
||||
constraintSet.clear(R.id.charm_view, ConstraintSet.TOP);
|
||||
// constraintSet.clear(R.id.charm_view, ConstraintSet.START);
|
||||
// constraintSet.clear(R.id.charm_view, ConstraintSet.END);
|
||||
|
||||
// 设置新约束(与 iv_tag_type 相同的位置)
|
||||
constraintSet.connect(
|
||||
R.id.charm_view, ConstraintSet.TOP,
|
||||
R.id.riv, ConstraintSet.BOTTOM,
|
||||
-dip2px(12) // 与 iv_tag_type 相同的 margin
|
||||
);
|
||||
|
||||
// constraintSet.connect(
|
||||
// R.id.charm_view, ConstraintSet.START,
|
||||
// R.id.riv, ConstraintSet.START,
|
||||
// 0
|
||||
// );
|
||||
//
|
||||
// constraintSet.connect(
|
||||
// R.id.charm_view, ConstraintSet.END,
|
||||
// R.id.riv, ConstraintSet.END,
|
||||
// 0
|
||||
// );
|
||||
mCharmView.setBg(R.drawable.room_bg_wheat_jy_charm);
|
||||
} else {
|
||||
// 恢复原始位置(在 tv_name 下面)
|
||||
// 显示 iv_tag_type(如果需要)
|
||||
if (iv_tag_type != null) {
|
||||
iv_tag_type.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
// 清除临时约束
|
||||
constraintSet.clear(R.id.charm_view, ConstraintSet.TOP);
|
||||
constraintSet.clear(R.id.charm_view, ConstraintSet.START);
|
||||
constraintSet.clear(R.id.charm_view, ConstraintSet.END);
|
||||
|
||||
// 恢复原始约束
|
||||
constraintSet.connect(
|
||||
R.id.charm_view, ConstraintSet.TOP,
|
||||
R.id.tv_name, ConstraintSet.BOTTOM,
|
||||
0
|
||||
);
|
||||
|
||||
constraintSet.connect(
|
||||
R.id.charm_view, ConstraintSet.START,
|
||||
R.id.riv, ConstraintSet.START,
|
||||
0
|
||||
);
|
||||
|
||||
constraintSet.connect(
|
||||
R.id.charm_view, ConstraintSet.END,
|
||||
R.id.riv, ConstraintSet.END,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
constraintSet.applyTo(constraintLayout);
|
||||
}
|
||||
|
||||
// 添加dp转px的工具方法
|
||||
private int dip2px(float dpValue) {
|
||||
final float scale = getContext().getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 开启计时
|
||||
*/
|
||||
public void setTime(int time) {
|
||||
if (time == 0) {
|
||||
mTvTime.setText("");
|
||||
mTvTime.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mTvTime.setText(String.format("%s'%s", time / 60, time % 60));
|
||||
mTvTime.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteSoundLevelUpdate(String userId, int soundLevel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalSoundLevelUpdate(int volume) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userJoined(int userId, int elapsd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userOffline(int userId, int reason) {
|
||||
|
||||
}
|
||||
|
||||
public void setOnlineStatus(UserOnlineStatusBean isOnline) {
|
||||
if (pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0") && !pitBean.getUser_id().isEmpty()) {
|
||||
if (pitBean.getUser_id().equals(isOnline.getUser_id())) {
|
||||
if (isOnline.getIs_online() == 1) {
|
||||
iv_on_line.setVisibility(GONE);
|
||||
} else {
|
||||
iv_on_line.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置是否显示助力按钮
|
||||
public void setLockZl(boolean lock) {
|
||||
tv_zhul.setVisibility(lock ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
|
||||
private OnZhulClickListener mOnZhulClickListener;
|
||||
|
||||
// 设置监听器的方法
|
||||
public void setOnZhulClickListener(OnZhulClickListener listener) {
|
||||
this.mOnZhulClickListener = listener;
|
||||
}
|
||||
|
||||
public interface OnZhulClickListener {
|
||||
void onZhulClick(RoomFriendshipWheatView view, RoomPitBean pitBean);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.UserInfo;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
/**
|
||||
*@author qx
|
||||
*@data 2025/8/13
|
||||
*@description: K歌模式下的视图
|
||||
*/
|
||||
public class RoomKtvWheatView extends BaseWheatView {
|
||||
public ImageView mIvTagBoss;
|
||||
public TextView mTvTime;
|
||||
public TextView tv_time_pk;
|
||||
|
||||
private boolean showBoss;//显示老板标识
|
||||
|
||||
public RoomKtvWheatView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public RoomKtvWheatView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RoomKtvWheatView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initPit(Context context, AttributeSet attrs) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoomDefaultWheatView);
|
||||
pitNumber = typedArray.getString(R.styleable.RoomDefaultWheatView_room_wheat_number);
|
||||
typedArray.recycle();
|
||||
mIvTagBoss = findViewById(R.id.iv_tag_boos);
|
||||
mTvTime = findViewById(R.id.tv_time);
|
||||
tv_time_pk = findViewById(R.id.tv_time_pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.room_view_ktv_wheat;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setPitData(RoomPitBean bean) {
|
||||
sex = bean.getSex();
|
||||
if (isOn()) {
|
||||
//开启声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
mTvName.setText(bean.getNickname());
|
||||
ImageUtils.loadHeadCC(bean.getAvatar(), mRiv);
|
||||
if (TextUtils.isEmpty(pitBean.getDress())) {
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mIvFrame.setVisibility(VISIBLE);
|
||||
mIvFrame.setSource(pitBean.getDress(), 1);
|
||||
// ImageUtils.loadDecorationAvatar(pitBean.getDress_picture(), mIvFrame);
|
||||
}
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(GONE);
|
||||
}
|
||||
} else {
|
||||
mTvName.setText(
|
||||
"-1".equals(pitNumber) ? "" :
|
||||
"9".equals(pitNumber) ? "主持位" :
|
||||
"10".equals(pitNumber) ? "嘉宾位" :
|
||||
pitNumber + "号麦位"
|
||||
);
|
||||
//麦位上锁
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(VISIBLE);
|
||||
ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default, mRiv);
|
||||
} else {
|
||||
// mIvTagBoss.setVisibility(GONE);
|
||||
// @DrawableRes int origin = getOriginImage();
|
||||
// ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo :
|
||||
// (origin == 0 ? R.mipmap.room_ic_wheat_default : origin), mRiv);
|
||||
mRiv.setImageResource(bean.getIs_lock() == 1 ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default);
|
||||
// ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default, mRiv);
|
||||
}
|
||||
if (isMute()) {
|
||||
ImageUtils.loadRes(R.mipmap.room_microphone_off, mIvSex);
|
||||
}
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
mIvFace.remove();
|
||||
//停止声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(GONE);
|
||||
}
|
||||
if (showSexIcon) {
|
||||
checkSex();
|
||||
}
|
||||
if (pitBean.getNickname() == null || pitBean.getNickname().isEmpty()) {
|
||||
mCharmView.setVisibility(GONE);
|
||||
} else {
|
||||
mCharmView.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
if (pitBean.is_pk() ){
|
||||
if (pitBean.getUser_id()!=null && !pitBean.getUser_id().equals("0") && !pitBean.getUser_id().isEmpty()) {
|
||||
tv_time_pk.setVisibility(VISIBLE);
|
||||
setSex(pitBean.getCharm(),false);
|
||||
mCharmView.setVisibility(GONE);
|
||||
}else {
|
||||
tv_time_pk.setVisibility(GONE);
|
||||
}
|
||||
// ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mCharmView.getLayoutParams();
|
||||
// params.width = 35;
|
||||
// mCharmView.setLayoutParams(params);
|
||||
|
||||
}else {
|
||||
tv_time_pk.setVisibility(GONE);
|
||||
mCharmView.setVisibility(VISIBLE);
|
||||
// ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mCharmView.getLayoutParams();
|
||||
// params.width = 52;
|
||||
// mCharmView.setLayoutParams(params);
|
||||
}
|
||||
|
||||
// setCardiac(pitBean.getPit_number(), 0.0f);
|
||||
}
|
||||
public void setSex( String value, boolean format) {
|
||||
if (format) {
|
||||
tv_time_pk.setText(value);
|
||||
} else {
|
||||
try {
|
||||
long xd = Long.parseLong(value);
|
||||
if (xd > 9999 || xd < -9999) {
|
||||
tv_time_pk.setText(String.format("%.2fw", xd / 10000.0f));
|
||||
// mBinding.tvValue.setText(String.valueOf(xd));
|
||||
} else {
|
||||
tv_time_pk.setText(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean showSexIcon = false;
|
||||
private String sex;
|
||||
|
||||
public boolean isMale() {
|
||||
return "1".equals(sex);
|
||||
}
|
||||
|
||||
public boolean isFemale() {
|
||||
return "2".equals(sex);
|
||||
}
|
||||
|
||||
public void setShowSexIcon(boolean show) {
|
||||
showSexIcon = show;
|
||||
}
|
||||
|
||||
public void checkSex() {
|
||||
if (isOn()) {
|
||||
mIvSex.setVisibility(VISIBLE);
|
||||
if (!TextUtils.isEmpty(sex)) {
|
||||
if (UserInfo.MALE.equals(sex)) {
|
||||
mIvSex.setBackgroundResource(R.drawable.room_xq_wheat_male_mask);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(R.mipmap.ic_room_xq_wno_male);
|
||||
} else {
|
||||
mIvSex.setBackgroundResource(R.drawable.room_xq_wheat_female_mask);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(R.mipmap.ic_room_xq_wno_female);
|
||||
}
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示老板标识
|
||||
*/
|
||||
public void setIsBossShow(String is_boss_pit) {
|
||||
showBoss = "1".equals(is_boss_pit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启计时
|
||||
*/
|
||||
public void setTime(int time) {
|
||||
if (time == 0) {
|
||||
mTvTime.setText("");
|
||||
mTvTime.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mTvTime.setText(String.format("%s'%s", time / 60, time % 60));
|
||||
mTvTime.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMaoziIcon() {
|
||||
View maozi = findViewById(R.id.iv_maozi);
|
||||
if (maozi != null) maozi.setVisibility(GONE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRemoteSoundLevelUpdate(String userId, int soundLevel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalSoundLevelUpdate(int volume) {
|
||||
if (volume==0){
|
||||
mIvRipple.stopAnimation();
|
||||
} else {
|
||||
if (pitBean.getUser_id().equals(SpUtil.getUserId()) && closePhone) {
|
||||
mIvRipple.stopAnimation();
|
||||
}else {
|
||||
mIvRipple.post(() -> {
|
||||
if (!mIvRipple.isAnimating()) {
|
||||
mIvRipple.startAnimation();
|
||||
}
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userJoined(int userId, int elapsd) {
|
||||
if (pitBean != null && pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0")) {
|
||||
if (pitBean.getUser_id().equals(userId + "")) {
|
||||
iv_on_line.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userOffline(int userId, int reason) {
|
||||
if (pitBean != null && pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0")) {
|
||||
if (pitBean.getUser_id().equals(userId + "")) {
|
||||
iv_on_line.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.UserOnlineStatusBean;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
|
||||
public class RoomMakeWheatView extends BaseWheatView {
|
||||
|
||||
public ImageView mIvTagBoss;
|
||||
public TextView mTvTime;
|
||||
public ImageView iv_zhul;
|
||||
public ImageView iv_on_line;
|
||||
private ImageView iv_tag_type;
|
||||
public WheatCharmView mCharmView;
|
||||
private boolean showBoss;//显示老板标识
|
||||
|
||||
private boolean showSexIcon = false;
|
||||
private String sex;
|
||||
|
||||
public RoomMakeWheatView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public RoomMakeWheatView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RoomMakeWheatView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initPit(Context context, AttributeSet attrs) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoomMakeWheatView);
|
||||
pitNumber = typedArray.getString(R.styleable.RoomMakeWheatView_room_make_wheat_number);
|
||||
pitImageVId=typedArray.getResourceId(R.styleable.RoomMakeWheatView_room_make_pic, 0);
|
||||
typedArray.recycle();
|
||||
mIvTagBoss = findViewById(R.id.iv_tag_boos);
|
||||
mTvTime = findViewById(R.id.tv_time);
|
||||
iv_on_line=findViewById(R.id.iv_online);
|
||||
iv_tag_type=findViewById(R.id.iv_tag_type);
|
||||
mCharmView = findViewById(R.id.charm_view);
|
||||
mRiv.setImageResource(pitImageVId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.room_view_make_wheat;
|
||||
}
|
||||
|
||||
public void setShowZl(boolean show) {
|
||||
if (iv_zhul != null) {
|
||||
iv_zhul.setVisibility(show ? VISIBLE : GONE);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void setPitData(RoomPitBean bean) {
|
||||
sex = bean.getSex();
|
||||
if (isOn()) {
|
||||
//开启声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
mTvName.setText(bean.getNickname());
|
||||
ImageUtils.loadCenterCrop(bean.getAvatar(), mRiv);
|
||||
if (TextUtils.isEmpty(pitBean.getDress_picture())) {
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mIvFrame.setVisibility(VISIBLE);
|
||||
}
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(GONE);
|
||||
}
|
||||
} else {
|
||||
mTvName.setText((!"8".equals(pitBean.getPit_number()) && !"9".equals(pitBean.getPit_number()))? pitBean.getPit_number() : "");
|
||||
//麦位上锁
|
||||
//麦位上锁
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(VISIBLE);
|
||||
ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default, mRiv);
|
||||
} else {
|
||||
mRiv.setImageResource(bean.getIs_lock()==1 ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default);
|
||||
}
|
||||
if (isMute()){
|
||||
ImageUtils.loadRes(R.mipmap.room_microphone_off, mIvSex);
|
||||
}
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
mIvFace.remove();
|
||||
//停止声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(GONE);
|
||||
}
|
||||
if (showSexIcon) {
|
||||
checkSex();
|
||||
}
|
||||
// if (pitBean.getIs_online()==0 && pitBean.getUser_id()!=null && !pitBean.getUser_id().equals("0")){
|
||||
// iv_on_line.setVisibility(VISIBLE);
|
||||
// }else {
|
||||
// iv_on_line.setVisibility(GONE);
|
||||
// }
|
||||
if (pitBean.getPit_number().equals("888")){
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
mCharmView.setVisibility(GONE);
|
||||
mTvName.setText(pitBean.getNickname()!=null && !pitBean.getNickname().equals("")?pitBean.getNickname():"拍卖者");
|
||||
}else if (pitBean.getPit_number().equals("9")){
|
||||
iv_tag_type.setVisibility(VISIBLE);
|
||||
mCharmView.setVisibility(GONE);
|
||||
}else if (pitBean.getPit_number().equals("111") || pitBean.getPit_number().equals("222")|| pitBean.getPit_number().equals("333")){
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
mTvName.setVisibility(GONE);
|
||||
mCharmView.setVisibility(GONE);
|
||||
if (pitBean.getUser_id()==null || pitBean.getUser_id().equals("0") ||pitBean.getUser_id().isEmpty()){
|
||||
mTvTime.setVisibility(GONE);
|
||||
}else {
|
||||
mTvTime.setVisibility(VISIBLE);
|
||||
}
|
||||
mTvTime.setText(pitBean.getCharm());
|
||||
}else if (pitBean.getPit_number().equals("000")){
|
||||
iv_tag_type.setVisibility(GONE);
|
||||
mCharmView.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setChe(String che){
|
||||
|
||||
}
|
||||
|
||||
public boolean isMale() {
|
||||
return "1".equals(sex);
|
||||
}
|
||||
|
||||
public boolean isFemale() {
|
||||
return "2".equals(sex);
|
||||
}
|
||||
|
||||
public void setShowSexIcon(boolean show) {
|
||||
showSexIcon = show;
|
||||
}
|
||||
|
||||
public void checkSex() {
|
||||
if (isOn()) {
|
||||
mIvSex.setVisibility(VISIBLE);
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示老板标识
|
||||
*/
|
||||
public void setIsBossShow(String is_boss_pit) {
|
||||
showBoss = "1".equals(is_boss_pit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启计时
|
||||
*/
|
||||
public void setTime(int time) {
|
||||
if (time == 0) {
|
||||
mTvTime.setText("");
|
||||
mTvTime.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mTvTime.setText(String.format("%s'%s", time / 60, time % 60));
|
||||
mTvTime.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMaoziIcon() {
|
||||
View maozi = findViewById(R.id.iv_maozi);
|
||||
if (maozi != null) maozi.setVisibility(GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteSoundLevelUpdate(String userId, int soundLevel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalSoundLevelUpdate(int volume) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userJoined(int userId, int elapsd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userOffline(int userId, int reason) {
|
||||
|
||||
}
|
||||
|
||||
public void setOnlineStatus(UserOnlineStatusBean isOnline) {
|
||||
if (pitBean.getUser_id() != null && !pitBean.getUser_id().equals("0") && !pitBean.getUser_id().isEmpty()) {
|
||||
if (pitBean.getUser_id().equals(isOnline.getUser_id())) {
|
||||
if (isOnline.getIs_online() == 1) {
|
||||
iv_on_line.setVisibility(GONE);
|
||||
} else {
|
||||
iv_on_line.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.CountDownTimer;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.blankj.utilcode.util.KeyboardUtils;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.RoomInputEvent;
|
||||
import com.xscm.moduleutil.event.RoomInputHideEvent;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
public class RoomMessageInputMenu extends ConstraintLayout {
|
||||
EditText etContent;
|
||||
Button tvSend;
|
||||
private View rootView;
|
||||
private int screenHeight;
|
||||
public RoomMessageInputMenu(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public RoomMessageInputMenu(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
LayoutInflater.from(context).inflate(R.layout.room_message_input_menu, this);
|
||||
etContent = findViewById(R.id.et_content);
|
||||
tvSend = findViewById(R.id.tv_send);
|
||||
tvSend.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onViewClicked(v);
|
||||
}
|
||||
});
|
||||
setVisibility(GONE);
|
||||
|
||||
}
|
||||
private void moveInputMenuAboveKeyboard(int keypadHeight) {
|
||||
// 调整输入框的位置
|
||||
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) this.getLayoutParams();
|
||||
params.bottomMargin = keypadHeight;
|
||||
this.setLayoutParams(params);
|
||||
}
|
||||
public void setText(String text) {
|
||||
etContent.setText(text);
|
||||
}
|
||||
|
||||
public void onViewClicked(View view) {
|
||||
if (!canSend) {
|
||||
ToastUtils.show("消息发送较频繁~");
|
||||
return;
|
||||
}
|
||||
String text = etContent.getText().toString();
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
ToastUtils.show("请输入评论内容");
|
||||
return;
|
||||
}
|
||||
// ApiClient.getInstance().sendTxtFilter(text,new BaseObserver<String>() {
|
||||
// @Override
|
||||
// public void onSubscribe(Disposable d) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onNext(String s) {
|
||||
// EventBus.getDefault().post(new RoomInputEvent(text));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onComplete() {
|
||||
//
|
||||
// }
|
||||
// });
|
||||
sendInput(text);
|
||||
dismiss();
|
||||
etContent.setText("");
|
||||
countDownTimer();
|
||||
|
||||
}
|
||||
private void sendInput(String text) {
|
||||
EventBus.getDefault().post(new RoomInputEvent(text));
|
||||
}
|
||||
/**
|
||||
* 下面的内容为发送消息逻辑
|
||||
*/
|
||||
private CountDownTimer mCountDownTimer;
|
||||
private boolean canSend = true;
|
||||
|
||||
private void countDownTimer() {
|
||||
releaseCountDownTimer();
|
||||
mCountDownTimer = new CountDownTimer(3 * 1000L, 1000L) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
canSend = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
canSend = true;
|
||||
}
|
||||
};
|
||||
mCountDownTimer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
releaseCountDownTimer();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
private void releaseCountDownTimer() {
|
||||
if (mCountDownTimer != null) {
|
||||
mCountDownTimer.cancel();
|
||||
mCountDownTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void show() {
|
||||
setVisibility(VISIBLE);
|
||||
etContent.requestFocus();
|
||||
KeyboardUtils.showSoftInput(etContent);
|
||||
EventBus.getDefault().post(new RoomInputHideEvent(false));
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
setVisibility(GONE);
|
||||
KeyboardUtils.hideSoftInput(etContent);
|
||||
EventBus.getDefault().post(new RoomInputHideEvent(true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.UserInfo;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
|
||||
/**
|
||||
* @Author lxj$
|
||||
* @Time 2025-8-6 17:27:29$ $
|
||||
* @Description 二卡八视图控件$
|
||||
*/
|
||||
public class RoomSingSongWheatView extends BaseWheatView {
|
||||
public ImageView mIvTagBoss;
|
||||
public TextView mTvTime;
|
||||
public TextView tv_time_pk;
|
||||
|
||||
private boolean showBoss;//显示老板标识
|
||||
|
||||
public RoomSingSongWheatView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public RoomSingSongWheatView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RoomSingSongWheatView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initPit(Context context, AttributeSet attrs) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoomDefaultWheatView);
|
||||
pitNumber = typedArray.getString(R.styleable.RoomDefaultWheatView_room_wheat_number);
|
||||
typedArray.recycle();
|
||||
mIvTagBoss = findViewById(R.id.iv_tag_boos);
|
||||
mTvTime = findViewById(R.id.tv_time);
|
||||
tv_time_pk = findViewById(R.id.tv_time_pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.room_view_sing_wheat;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setPitData(RoomPitBean bean) {
|
||||
sex = bean.getSex();
|
||||
if (isOn()) {
|
||||
//开启声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
mTvName.setText(bean.getNickname());
|
||||
ImageUtils.loadHeadCC(bean.getAvatar(), mRiv);
|
||||
if (TextUtils.isEmpty(pitBean.getDress())) {
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mIvFrame.setVisibility(VISIBLE);
|
||||
mIvFrame.setSource(pitBean.getDress(), 1);
|
||||
// ImageUtils.loadDecorationAvatar(pitBean.getDress_picture(), mIvFrame);
|
||||
}
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(GONE);
|
||||
}
|
||||
} else {
|
||||
mTvName.setText(
|
||||
"-1".equals(pitNumber) ? "" :
|
||||
"9".equals(pitNumber) ? "主持位" :
|
||||
"10".equals(pitNumber) ? "嘉宾位" :
|
||||
pitNumber + "号麦位"
|
||||
);
|
||||
//麦位上锁
|
||||
if (showBoss && WHEAT_BOSS.equals(pitNumber)) {
|
||||
mIvTagBoss.setVisibility(VISIBLE);
|
||||
ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default, mRiv);
|
||||
} else {
|
||||
// mIvTagBoss.setVisibility(GONE);
|
||||
// @DrawableRes int origin = getOriginImage();
|
||||
// ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo :
|
||||
// (origin == 0 ? R.mipmap.room_ic_wheat_default : origin), mRiv);
|
||||
mRiv.setImageResource(bean.getIs_lock() == 1 ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default);
|
||||
// ImageUtils.loadRes(isLocked() ? R.mipmap.room_ic_wheat_default_suo : R.mipmap.room_ic_wheat_default, mRiv);
|
||||
}
|
||||
if (isMute()) {
|
||||
ImageUtils.loadRes(R.mipmap.room_microphone_off, mIvSex);
|
||||
}
|
||||
mIvFrame.setVisibility(INVISIBLE);
|
||||
mIvFace.remove();
|
||||
//停止声浪
|
||||
mIvRipple.stopAnimation();
|
||||
mIvRipple.setVisibility(GONE);
|
||||
}
|
||||
if (showSexIcon) {
|
||||
checkSex();
|
||||
}
|
||||
if (pitBean.getNickname() == null || pitBean.getNickname().isEmpty()) {
|
||||
mCharmView.setVisibility(GONE);
|
||||
} else {
|
||||
mCharmView.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
if (pitBean.is_pk() ){
|
||||
if (pitBean.getUser_id()!=null && !pitBean.getUser_id().equals("0") && !pitBean.getUser_id().isEmpty()) {
|
||||
tv_time_pk.setVisibility(VISIBLE);
|
||||
setSex(pitBean.getCharm(),false);
|
||||
mCharmView.setVisibility(GONE);
|
||||
}else {
|
||||
tv_time_pk.setVisibility(GONE);
|
||||
}
|
||||
// ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mCharmView.getLayoutParams();
|
||||
// params.width = 35;
|
||||
// mCharmView.setLayoutParams(params);
|
||||
|
||||
}else {
|
||||
tv_time_pk.setVisibility(GONE);
|
||||
mCharmView.setVisibility(VISIBLE);
|
||||
// ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mCharmView.getLayoutParams();
|
||||
// params.width = 52;
|
||||
// mCharmView.setLayoutParams(params);
|
||||
}
|
||||
|
||||
// setCardiac(pitBean.getPit_number(), 0.0f);
|
||||
}
|
||||
public void setSex( String value, boolean format) {
|
||||
if (format) {
|
||||
tv_time_pk.setText(value);
|
||||
} else {
|
||||
try {
|
||||
long xd = Long.parseLong(value);
|
||||
if (xd > 9999 || xd < -9999) {
|
||||
tv_time_pk.setText(String.format("%.2fw", xd / 10000.0f));
|
||||
// mBinding.tvValue.setText(String.valueOf(xd));
|
||||
} else {
|
||||
tv_time_pk.setText(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean showSexIcon = false;
|
||||
private String sex;
|
||||
|
||||
public boolean isMale() {
|
||||
return "1".equals(sex);
|
||||
}
|
||||
|
||||
public boolean isFemale() {
|
||||
return "2".equals(sex);
|
||||
}
|
||||
|
||||
public void setShowSexIcon(boolean show) {
|
||||
showSexIcon = show;
|
||||
}
|
||||
|
||||
public void checkSex() {
|
||||
if (isOn()) {
|
||||
mIvSex.setVisibility(VISIBLE);
|
||||
if (!TextUtils.isEmpty(sex)) {
|
||||
if (UserInfo.MALE.equals(sex)) {
|
||||
mIvSex.setBackgroundResource(R.drawable.room_xq_wheat_male_mask);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(R.mipmap.ic_room_xq_wno_male);
|
||||
} else {
|
||||
mIvSex.setBackgroundResource(R.drawable.room_xq_wheat_female_mask);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(R.mipmap.ic_room_xq_wno_female);
|
||||
}
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
} else {
|
||||
mIvSex.setVisibility(GONE);
|
||||
if (mTvNo != null) mTvNo.setBackgroundResource(getOriginNoImage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示老板标识
|
||||
*/
|
||||
public void setIsBossShow(String is_boss_pit) {
|
||||
showBoss = "1".equals(is_boss_pit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启计时
|
||||
*/
|
||||
public void setTime(int time) {
|
||||
if (time == 0) {
|
||||
mTvTime.setText("");
|
||||
mTvTime.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
mTvTime.setText(String.format("%s'%s", time / 60, time % 60));
|
||||
mTvTime.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMaoziIcon() {
|
||||
View maozi = findViewById(R.id.iv_maozi);
|
||||
if (maozi != null) maozi.setVisibility(GONE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRemoteSoundLevelUpdate(String userId, int soundLevel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalSoundLevelUpdate(int volume) {
|
||||
if (volume==0){
|
||||
mIvRipple.stopAnimation();
|
||||
} else {
|
||||
if (pitBean.getUser_id().equals(SpUtil.getUserId()) && closePhone) {
|
||||
mIvRipple.stopAnimation();
|
||||
}else {
|
||||
mIvRipple.post(() -> {
|
||||
if (!mIvRipple.isAnimating()) {
|
||||
mIvRipple.startAnimation();
|
||||
}
|
||||
mIvRipple.setVisibility(VISIBLE);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userJoined(int userId, int elapsd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userOffline(int userId, int reason) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Shader;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* ProjectName: CustomViewApp
|
||||
* Package: com.yde.libview
|
||||
* Description: 扫光TextView
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/3/11
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/3/11 10:58
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class ScanTextView extends TextView {
|
||||
|
||||
//渲染器
|
||||
private LinearGradient mLinearGradient;
|
||||
//渲染范围
|
||||
private Matrix mGradientMatrix;
|
||||
//渲染的起始位置
|
||||
private int mViewWidth = 0;
|
||||
//渲染的终止距离
|
||||
private int mTranslate = 0;
|
||||
//是否启动动画
|
||||
private boolean mAnimating = true;
|
||||
//多少毫秒刷新一次
|
||||
private int speed = 50;
|
||||
private int lightColor;
|
||||
private Paint mPaint;
|
||||
|
||||
public ScanTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mPaint = getPaint();
|
||||
mGradientMatrix = new Matrix();
|
||||
}
|
||||
|
||||
@SuppressLint("DrawAllocation")
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
mViewWidth = getMeasuredWidth();
|
||||
// mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[] { Color.GRAY, Color.WHITE, Color.GRAY }, null, Shader.TileMode.CLAMP);
|
||||
if (mAnimating) {
|
||||
mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{getCurrentTextColor(), lightColor, getCurrentTextColor()}, null, Shader.TileMode.CLAMP);
|
||||
} else {
|
||||
mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{getCurrentTextColor(), getCurrentTextColor(), getCurrentTextColor()}, null, Shader.TileMode.CLAMP);
|
||||
}
|
||||
mPaint.setShader(mLinearGradient);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (mAnimating && mGradientMatrix != null) {
|
||||
mTranslate += mViewWidth / 10;//每次移动屏幕的1/10宽
|
||||
if (mTranslate > 2 * mViewWidth) {
|
||||
mTranslate = -mViewWidth;
|
||||
}
|
||||
mGradientMatrix.setTranslate(mTranslate, 0);
|
||||
mLinearGradient.setLocalMatrix(mGradientMatrix);//在指定矩阵上渲染
|
||||
postInvalidateDelayed(speed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
}
|
||||
|
||||
public void setSpeed(int speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
public void setLightColor(int lightColor) {
|
||||
this.lightColor = lightColor;
|
||||
}
|
||||
|
||||
public void setScan(boolean canScan) {
|
||||
this.mAnimating = canScan;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
/**
|
||||
* Created by Administrator on 2017/5/17.
|
||||
*/
|
||||
|
||||
public class ScrollViewPager extends ViewPager {
|
||||
private boolean scroll = true;
|
||||
|
||||
public ScrollViewPager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ScrollViewPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setScroll(boolean scroll) {
|
||||
this.scroll = scroll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollTo(int x, int y) {
|
||||
super.scrollTo(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent arg0) {
|
||||
/*return false;//super.onTouchEvent(arg0);*/
|
||||
if (scroll)
|
||||
return false;
|
||||
else
|
||||
return super.onTouchEvent(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent arg0) {
|
||||
if (scroll)
|
||||
return false;
|
||||
else
|
||||
return super.onInterceptTouchEvent(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentItem(int item, boolean smoothScroll) {
|
||||
super.setCurrentItem(item, smoothScroll);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentItem(int item) {
|
||||
super.setCurrentItem(item);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.util.Log;
|
||||
|
||||
public class ShaderUtils {
|
||||
|
||||
private static final String TAG = "ShaderUtils";
|
||||
|
||||
// 创建并编译着色器
|
||||
public static int loadShader(int type, String shaderCode) {
|
||||
int shader = GLES20.glCreateShader(type);
|
||||
GLES20.glShaderSource(shader, shaderCode);
|
||||
GLES20.glCompileShader(shader);
|
||||
|
||||
int[] compiled = new int[1];
|
||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
|
||||
if (compiled[0] == 0) {
|
||||
Log.e(TAG, "Could NOT compile shader: " + GLES20.glGetShaderInfoLog(shader));
|
||||
GLES20.glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
// 创建 FloatBuffer
|
||||
public static FloatBuffer createFloatBuffer(float[] array) {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(array.length * 4);
|
||||
bb.order(ByteOrder.nativeOrder());
|
||||
FloatBuffer buffer = bb.asFloatBuffer();
|
||||
buffer.put(array);
|
||||
buffer.position(0);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.os.CountDownTimer;
|
||||
|
||||
public class SilentCountDownTimer {
|
||||
private CountDownTimer countDownTimer;
|
||||
private long remainingMillis; // 剩余毫秒数
|
||||
private OnCountDownFinishListener listener;
|
||||
|
||||
public interface OnCountDownFinishListener {
|
||||
void onFinish();
|
||||
}
|
||||
|
||||
public void setOnCountDownFinishListener(OnCountDownFinishListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动或更新倒计时
|
||||
*
|
||||
* @param targetTimestamp 目标时间戳(单位:秒)
|
||||
*/
|
||||
public void start(long targetTimestamp) {
|
||||
// 将目标时间戳(秒)转为毫秒,并计算剩余时间
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
long targetMillis = targetTimestamp * 1000L;
|
||||
remainingMillis = targetMillis - currentTimeMillis;
|
||||
|
||||
if (remainingMillis <= 0) {
|
||||
// 时间已过,直接结束
|
||||
finishCountDown();
|
||||
return;
|
||||
}
|
||||
|
||||
// 释放旧的倒计时
|
||||
if (countDownTimer != null) {
|
||||
countDownTimer.cancel();
|
||||
}
|
||||
|
||||
// 创建新倒计时
|
||||
countDownTimer = new CountDownTimer(remainingMillis, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
remainingMillis = millisUntilFinished;
|
||||
// 可选:输出日志或做一些后台处理
|
||||
// Log.d("SilentCountDown", "剩余时间:" + millisUntilFinished / 1000 + " 秒");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
finishCountDown();
|
||||
}
|
||||
};
|
||||
|
||||
countDownTimer.start();
|
||||
}
|
||||
|
||||
private void finishCountDown() {
|
||||
if (listener != null) {
|
||||
listener.onFinish(); // 回调完成
|
||||
}
|
||||
release(); // 释放资源
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁倒计时并释放资源
|
||||
*/
|
||||
public void release() {
|
||||
if (countDownTimer != null) {
|
||||
countDownTimer.cancel();
|
||||
countDownTimer = null;
|
||||
}
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,640 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.text.InputFilter;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.InflateException;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
public class SplitEditText extends AppCompatEditText {
|
||||
//密码显示模式:隐藏密码,显示圆形
|
||||
public static final int CONTENT_SHOW_MODE_PASSWORD = 1;
|
||||
//密码显示模式:显示密码
|
||||
public static final int CONTENT_SHOW_MODE_TEXT = 2;
|
||||
//输入框相连的样式
|
||||
public static final int INPUT_BOX_STYLE_CONNECT = 1;
|
||||
//单个的输入框样式
|
||||
public static final int INPUT_BOX_STYLE_SINGLE = 2;
|
||||
//下划线输入框样式
|
||||
public static final int INPUT_BOX_STYLE_UNDERLINE = 3;
|
||||
//画笔
|
||||
private RectF mRectFConnect;
|
||||
private RectF mRectFSingleBox;
|
||||
private Paint mPaintDivisionLine;
|
||||
private Paint mPaintContent;
|
||||
private Paint mPaintBorder;
|
||||
private Paint mPaintUnderline;
|
||||
//边框大小
|
||||
private Float mBorderSize;
|
||||
//边框颜色
|
||||
private int mBorderColor;
|
||||
//圆角大小
|
||||
private float mCornerSize;
|
||||
//分割线大小
|
||||
private float mDivisionLineSize;
|
||||
//分割线颜色
|
||||
private int mDivisionColor;
|
||||
//圆形密码的半径大小
|
||||
private float mCircleRadius;
|
||||
//密码框长度
|
||||
private int mContentNumber;
|
||||
//密码显示模式
|
||||
private int mContentShowMode;
|
||||
//单框和下划线输入样式下,每个输入框的间距
|
||||
private float mSpaceSize;
|
||||
//输入框样式
|
||||
private int mInputBoxStyle;
|
||||
//字体大小
|
||||
private float mTextSize;
|
||||
//字体颜色
|
||||
private int mTextColor;
|
||||
//每个输入框是否是正方形标识
|
||||
private boolean mInputBoxSquare;
|
||||
private OnInputListener inputListener;
|
||||
private Paint mPaintCursor;
|
||||
private CursorRunnable cursorRunnable;
|
||||
|
||||
private int mCursorColor;//光标颜色
|
||||
private float mCursorWidth;//光标宽度
|
||||
private int mCursorHeight;//光标高度
|
||||
private int mCursorDuration;//光标闪烁时长
|
||||
|
||||
private int mUnderlineFocusColor;//下划线输入样式下,输入框获取焦点时下划线颜色
|
||||
private int mUnderlineNormalColor;//下划线输入样式下,下划线颜色
|
||||
|
||||
public SplitEditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
//这里没有写成默认的EditText属性样式android.R.attr.editTextStyle,这样会存在EditText默认的样式
|
||||
public SplitEditText(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
/*public SplitEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, android.R.attr.editTextStyle);
|
||||
initAttrs(context, attrs);
|
||||
}*/
|
||||
|
||||
public SplitEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initAttrs(context, attrs);
|
||||
}
|
||||
|
||||
private void initAttrs(Context c, AttributeSet attrs) {
|
||||
TypedArray array = c.obtainStyledAttributes(attrs, R.styleable.SplitEditText);
|
||||
mBorderSize = array.getDimension(R.styleable.SplitEditText_borderSize, dp2px(1f));
|
||||
mBorderColor = array.getColor(R.styleable.SplitEditText_borderColor, Color.BLACK);
|
||||
mCornerSize = array.getDimension(R.styleable.SplitEditText_corner_size, 0f);
|
||||
mDivisionLineSize = array.getDimension(R.styleable.SplitEditText_divisionLineSize, dp2px(1f));
|
||||
mDivisionColor = array.getColor(R.styleable.SplitEditText_divisionLineColor, Color.BLACK);
|
||||
mCircleRadius = array.getDimension(R.styleable.SplitEditText_circleRadius, dp2px(5f));
|
||||
mContentNumber = array.getInt(R.styleable.SplitEditText_contentNumber, 6);
|
||||
mContentShowMode = array.getInteger(R.styleable.SplitEditText_contentShowMode, CONTENT_SHOW_MODE_PASSWORD);
|
||||
mInputBoxStyle = array.getInteger(R.styleable.SplitEditText_inputBoxStyle, INPUT_BOX_STYLE_CONNECT);
|
||||
mSpaceSize = array.getDimension(R.styleable.SplitEditText_spaceSize, dp2px(10f));
|
||||
mTextSize = array.getDimension(R.styleable.SplitEditText_android_textSize, sp2px(16f));
|
||||
mTextColor = array.getColor(R.styleable.SplitEditText_android_textColor, Color.BLACK);
|
||||
mInputBoxSquare = array.getBoolean(R.styleable.SplitEditText_inputBoxSquare, true);
|
||||
mCursorColor = array.getColor(R.styleable.SplitEditText_cursorColor, ContextCompat.getColor(c,R.color.colorAccent));
|
||||
mCursorDuration = array.getInt(R.styleable.SplitEditText_cursorDuration, 500);
|
||||
mCursorWidth = array.getDimension(R.styleable.SplitEditText_cursorWidth, dp2px(2f));
|
||||
mCursorHeight = (int) array.getDimension(R.styleable.SplitEditText_cursorHeight, 0);
|
||||
mUnderlineNormalColor = array.getInt(R.styleable.SplitEditText_underlineNormalColor, Color.BLACK);
|
||||
mUnderlineFocusColor = array.getInt(R.styleable.SplitEditText_underlineFocusColor, 0);
|
||||
array.recycle();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mPaintBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintBorder.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
mPaintBorder.setStrokeWidth(mBorderSize);
|
||||
mPaintBorder.setColor(mBorderColor);
|
||||
|
||||
mPaintDivisionLine = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintDivisionLine.setStyle(Paint.Style.STROKE);
|
||||
mPaintDivisionLine.setStrokeWidth(mDivisionLineSize);
|
||||
mPaintDivisionLine.setColor(mDivisionColor);
|
||||
|
||||
mPaintContent = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintContent.setTextSize(mTextSize);
|
||||
|
||||
mPaintCursor = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintCursor.setStrokeWidth(mCursorWidth);
|
||||
mPaintCursor.setColor(mCursorColor);
|
||||
|
||||
mPaintUnderline = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintUnderline.setStrokeWidth(mBorderSize);
|
||||
mPaintUnderline.setColor(mUnderlineNormalColor);
|
||||
|
||||
|
||||
//避免onDraw里面重复创建RectF对象,先初始化RectF对象,在绘制时调用set()方法
|
||||
//单个输入框样式的RectF
|
||||
mRectFSingleBox = new RectF();
|
||||
//绘制Connect样式的矩形框
|
||||
mRectFConnect = new RectF();
|
||||
//设置单行输入
|
||||
setSingleLine();
|
||||
|
||||
//若构造方法中没有写成android.R.attr.editTextStyle的属性,应该需要设置该属性,EditText默认是获取焦点的
|
||||
setFocusableInTouchMode(true);
|
||||
|
||||
//取消默认的光标
|
||||
//这里默认不设置该属性,不然长按粘贴有问题(一开始长按不能粘贴,输入内容就可以长按粘贴)
|
||||
//setCursorVisible(false);
|
||||
|
||||
//设置透明光标,若是直接不显示光标的话,长按粘贴会没效果
|
||||
/*try {
|
||||
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
|
||||
f.setAccessible(true);
|
||||
f.set(this, 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
//设置光标的TextSelectHandle
|
||||
//这里判断版本,10.0以及以上直接通过方法调用,以下通过反射设置
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
// setTextSelectHandle(android.R.color.transparent);
|
||||
// } else {
|
||||
// //通过反射改变光标TextSelectHandle的样式
|
||||
// try {
|
||||
// Field f = TextView.class.getDeclaredField("mTextSelectHandleRes");
|
||||
// f.setAccessible(true);
|
||||
// f.set(this, android.R.color.transparent);
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
//设置InputFilter,设置输入的最大字符长度为设置的长度
|
||||
setFilters(new InputFilter[]{new InputFilter.LengthFilter(mContentNumber)});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
// L.e("SplitEditText","onAttachedToWindow------->");
|
||||
super.onAttachedToWindow();
|
||||
if(hasFocus()){
|
||||
startCursor();
|
||||
}
|
||||
}
|
||||
|
||||
private void startCursor(){
|
||||
if(cursorRunnable==null){
|
||||
cursorRunnable = new CursorRunnable();
|
||||
}
|
||||
postDelayed(cursorRunnable, mCursorDuration);
|
||||
}
|
||||
|
||||
private void stopCursor(){
|
||||
if(cursorRunnable!=null){
|
||||
removeCallbacks(cursorRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
|
||||
super.onFocusChanged(focused, direction, previouslyFocusedRect);
|
||||
if(focused){
|
||||
startCursor();
|
||||
}else{
|
||||
stopCursor();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
// L.e("SplitEditText","onDetachedFromWindow------->");
|
||||
stopCursor();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (mInputBoxSquare) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
//计算view高度,使view高度和每个item的宽度相等,确保每个item是一个正方形
|
||||
float itemWidth = getContentItemWidthOnMeasure(width);
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
setMeasuredDimension(width, (int) (itemWidth + mBorderSize));
|
||||
break;
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
default:
|
||||
setMeasuredDimension(width, (int) (itemWidth + mBorderSize * 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
//绘制输入框
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
drawSingleStyle(canvas);
|
||||
break;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
drawUnderlineStyle(canvas);
|
||||
break;
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
default:
|
||||
drawConnectStyle(canvas);
|
||||
break;
|
||||
}
|
||||
//绘制输入框内容
|
||||
drawContent(canvas);
|
||||
//绘制光标
|
||||
if(hasFocus()){
|
||||
drawCursor(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输入内容显示模式,绘制内容是圆心还是明文的text
|
||||
*/
|
||||
private void drawContent(Canvas canvas) {
|
||||
int cy = getHeight() / 2;
|
||||
String password = getText().toString().trim();
|
||||
if (mContentShowMode == CONTENT_SHOW_MODE_PASSWORD) {
|
||||
mPaintContent.setColor(Color.BLACK);
|
||||
for (int i = 0; i < password.length(); i++) {
|
||||
float startX = getDrawContentStartX(i);
|
||||
canvas.drawCircle(startX, cy, mCircleRadius, mPaintContent);
|
||||
}
|
||||
} else {
|
||||
mPaintContent.setColor(mTextColor);
|
||||
//计算baseline
|
||||
float baselineText = getTextBaseline(mPaintContent, cy);
|
||||
for (int i = 0; i < password.length(); i++) {
|
||||
float startX = getDrawContentStartX(i);
|
||||
//计算文字宽度
|
||||
String text = String.valueOf(password.charAt(i));
|
||||
float textWidth = mPaintContent.measureText(text);
|
||||
//绘制文字x应该还需要减去文字宽度的一半
|
||||
canvas.drawText(text, startX - textWidth / 2, baselineText, mPaintContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制光标
|
||||
* 光标只有一个,所以不需要根据循环来绘制,只需绘制第N个就行
|
||||
* 即:
|
||||
* 当输入内容长度为0,光标在第0个位置
|
||||
* 当输入内容长度为1,光标应在第1个位置
|
||||
* ...
|
||||
* 所以光标所在位置为输入内容的长度
|
||||
* 这里光标的长度默认就是 height/2
|
||||
*/
|
||||
private void drawCursor(Canvas canvas) {
|
||||
if (mCursorHeight > getHeight()) {
|
||||
throw new InflateException("cursor height must smaller than view height");
|
||||
}
|
||||
String content = getText().toString().trim();
|
||||
float startX = getDrawContentStartX(content.length());
|
||||
//如果设置得有光标高度,那么startY = (高度-光标高度)/2+边框宽度
|
||||
if (mCursorHeight == 0) {
|
||||
mCursorHeight = getHeight() / 2;
|
||||
}
|
||||
int sy = (getHeight() - mCursorHeight) / 2;
|
||||
float startY = sy + mBorderSize;
|
||||
float stopY = getHeight() - sy - mBorderSize;
|
||||
|
||||
//此时的绘制光标竖直线,startX = stopX
|
||||
canvas.drawLine(startX, startY, startX, stopY, mPaintCursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 、
|
||||
* 计算三种输入框样式下绘制圆和文字的x坐标
|
||||
*
|
||||
* @param index 循环里面的下标 i
|
||||
*/
|
||||
private float getDrawContentStartX(int index) {
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
//单个输入框样式下的startX
|
||||
//即 itemWidth/2 + i*itemWidth + i*每一个间距宽度 + 前面所有的左右边框
|
||||
// i = 0,左侧1个边框
|
||||
// i = 1,左侧3个边框(一个完整的item的左右边框+ 一个左侧边框)
|
||||
// i = ..., (2*i+1)*mBorderSize
|
||||
return getContentItemWidth() / 2 + index * getContentItemWidth() + index * mSpaceSize + (2 * index + 1) * mBorderSize;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
//下划线输入框样式下的startX
|
||||
//即 itemWidth/2 + i*itemWidth + i*每一个间距宽度
|
||||
return getContentItemWidth() / 2 + index * mSpaceSize + index * getContentItemWidth();
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
//矩形输入框样式下的startX
|
||||
//即 itemWidth/2 + i*itemWidth + i*分割线宽度 + 左侧的一个边框宽度
|
||||
default:
|
||||
return getContentItemWidth() / 2 + index * getContentItemWidth() + index * mDivisionLineSize + mBorderSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绘制下划线输入框样式
|
||||
* 线条起点startX:每个字符所占宽度itemWidth + 每个字符item之间的间距mSpaceSize
|
||||
* 线条终点stopX:stopX与startX之间就是一个itemWidth的宽度
|
||||
*/
|
||||
private void drawUnderlineStyle(Canvas canvas) {
|
||||
String content = getText().toString().trim();
|
||||
for (int i = 0; i < mContentNumber; i++) {
|
||||
//计算绘制下划线的startX
|
||||
float startX = i * getContentItemWidth() + i * mSpaceSize;
|
||||
//stopX
|
||||
float stopX = getContentItemWidth() + startX;
|
||||
//对于下划线这种样式,startY = stopY
|
||||
float startY = getHeight() - mBorderSize / 2;
|
||||
//这里判断是否设置有输入框获取焦点时,下划线的颜色
|
||||
if (mUnderlineFocusColor != 0) {
|
||||
if (content.length() >= i) {
|
||||
mPaintUnderline.setColor(mUnderlineFocusColor);
|
||||
} else {
|
||||
mPaintUnderline.setColor(mUnderlineNormalColor);
|
||||
}
|
||||
}
|
||||
canvas.drawLine(startX, startY, stopX, startY, mPaintUnderline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制单框输入模式
|
||||
* 这里计算left、right时有点饶,
|
||||
* 理解、计算时最好根据图形、参照drawConnectStyle()绘制带边框的矩形
|
||||
* left:绘制带边框的矩形等图形时,去掉边框的一半即 + mBorderSize / 2,同时加上每个字符item的间距 + i*mSpaceSize
|
||||
* 另外,每个字符item的宽度 + i*itemWidth
|
||||
* 最后,绘制时都是以整个view的宽度计算,绘制第N个时,都应该加上以前的边框宽度
|
||||
* 即第一个:i = 0 ,边框的宽度为0
|
||||
* 第二个:i = 1,边框的宽度 2*mBorderSize,左右两个的边框宽度
|
||||
* 以此...最后应该 + i*2*mBorderSize
|
||||
* 同理
|
||||
* right:去掉边框的一半: -mBorderSize/2,还应该加上前面一个item的宽度:+(i+1)*itemWidth
|
||||
* 同样,绘制时都是以整个view的宽度计算,绘制后面的,都应该加上前面的所有宽度
|
||||
* 即 间距:+i*mSpaceSize
|
||||
* 边框:(注意是计算整个view)
|
||||
* 第一个:i = 0,2个边框2*mBorderSize
|
||||
* 第二个:i = 1,4个边框,即 (1+1)*2*mBorderSize
|
||||
* 所以算上边框 +(i+1)*2*mBorderSize
|
||||
*/
|
||||
private void drawSingleStyle(Canvas canvas) {
|
||||
for (int i = 0; i < mContentNumber; i++) {
|
||||
mRectFSingleBox.setEmpty();
|
||||
float left = i * getContentItemWidth() + i * mSpaceSize + i * mBorderSize * 2 + mBorderSize / 2;
|
||||
float right = i * mSpaceSize + (i + 1) * getContentItemWidth() + (i + 1) * 2 * mBorderSize - mBorderSize / 2;
|
||||
//为避免在onDraw里面创建RectF对象,这里使用rectF.set()方法
|
||||
mRectFSingleBox.set(left, mBorderSize / 2, right, getHeight() - mBorderSize / 2);
|
||||
canvas.drawRoundRect(mRectFSingleBox, mCornerSize, mCornerSize, mPaintBorder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制矩形外框
|
||||
* 在绘制圆角矩形的时候,应该减掉边框的宽度
|
||||
* 不然会有所偏差
|
||||
* <p>
|
||||
* 在绘制矩形以及其它图形的时候,矩形(图形)的边界是边框的中心,不是边框的边界
|
||||
* 所以在绘制带边框的图形的时候应该减去边框宽度的一半
|
||||
* https://blog.csdn.net/a512337862/article/details/74161988
|
||||
*/
|
||||
private void drawConnectStyle(Canvas canvas) {
|
||||
//每次重新绘制时,先将rectF重置下
|
||||
mRectFConnect.setEmpty();
|
||||
//需要减去边框的一半
|
||||
mRectFConnect.set(
|
||||
mBorderSize / 2,
|
||||
mBorderSize / 2,
|
||||
getWidth() - mBorderSize / 2,
|
||||
getHeight() - mBorderSize / 2
|
||||
);
|
||||
canvas.drawRoundRect(mRectFConnect, mCornerSize, mCornerSize, mPaintBorder);
|
||||
//绘制分割线
|
||||
drawDivisionLine(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分割线条数为内容框数目-1
|
||||
* 这里startX应该要加上左侧边框的宽度
|
||||
* 应该还需要加上分割线的一半
|
||||
* 至于startY和stopY不是 mBorderSize/2 而是 mBorderSize
|
||||
* startX是计算整个宽度的,需要算上左侧的边框宽度,所以不是+mBorderSize/2 而是+mBorderSize
|
||||
* startY和stopY:分割线是紧挨着边框内部的,所以应该是mBorderSize,而不是mBorderSize/2
|
||||
*/
|
||||
private void drawDivisionLine(Canvas canvas) {
|
||||
float stopY = getHeight() - mBorderSize;
|
||||
for (int i = 0; i < mContentNumber - 1; i++) {
|
||||
//对于分割线条,startX = stopX
|
||||
float startX = (i + 1) * getContentItemWidth() + i * mDivisionLineSize + mBorderSize + mDivisionLineSize / 2;
|
||||
canvas.drawLine(startX, mBorderSize, startX, stopY, mPaintDivisionLine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算3种样式下,相应每个字符item的宽度
|
||||
*/
|
||||
private float getContentItemWidth() {
|
||||
//计算每个密码字符所占的宽度,每种输入框样式下,每个字符item所占宽度也不一样
|
||||
float tempWidth;
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
//单个输入框样式:宽度-间距宽度(字符数-1)*每个间距宽度-每个输入框的左右边框宽度
|
||||
tempWidth = getWidth() - (mContentNumber - 1) * mSpaceSize - 2 * mContentNumber * mBorderSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
//下划线样式:宽度-间距宽度(字符数-1)*每个间距宽度
|
||||
tempWidth = getWidth() - (mContentNumber - 1) * mSpaceSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
//矩形输入框样式:宽度-左右两边框宽度-分割线宽度(字符数-1)*每个分割线宽度
|
||||
default:
|
||||
tempWidth = getWidth() - (mDivisionLineSize * (mContentNumber - 1)) - 2 * mBorderSize;
|
||||
break;
|
||||
}
|
||||
return tempWidth / mContentNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据view的测量宽度,计算每个item的宽度
|
||||
*
|
||||
* @param measureWidth view的measure
|
||||
* @return onMeasure时的每个item宽度
|
||||
*/
|
||||
private float getContentItemWidthOnMeasure(int measureWidth) {
|
||||
//计算每个密码字符所占的宽度,每种输入框样式下,每个字符item所占宽度也不一样
|
||||
float tempWidth;
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
//单个输入框样式:宽度-间距宽度(字符数-1)*每个间距宽度-每个输入框的左右边框宽度
|
||||
tempWidth = measureWidth - (mContentNumber - 1) * mSpaceSize - 2 * mContentNumber * mBorderSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
//下划线样式:宽度-间距宽度(字符数-1)*每个间距宽度
|
||||
tempWidth = measureWidth - (mContentNumber - 1) * mSpaceSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
//矩形输入框样式:宽度-左右两边框宽度-分割线宽度(字符数-1)*每个分割线宽度
|
||||
default:
|
||||
tempWidth = measureWidth - (mDivisionLineSize * (mContentNumber - 1)) - 2 * mBorderSize;
|
||||
break;
|
||||
}
|
||||
return tempWidth / mContentNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算绘制文本的基线
|
||||
*
|
||||
* @param paint 绘制文字的画笔
|
||||
* @param halfHeight 高度的一半
|
||||
*/
|
||||
private float getTextBaseline(Paint paint, float halfHeight) {
|
||||
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
|
||||
float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
|
||||
return halfHeight + dy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置密码是否可见
|
||||
*/
|
||||
public void setContentShowMode(int mode) {
|
||||
if (mode != CONTENT_SHOW_MODE_PASSWORD && mode != CONTENT_SHOW_MODE_TEXT) {
|
||||
throw new IllegalArgumentException(
|
||||
"the value of the parameter must be one of" +
|
||||
"{1:EDIT_SHOW_MODE_PASSWORD} or " +
|
||||
"{2:EDIT_SHOW_MODE_TEXT}"
|
||||
);
|
||||
}
|
||||
mContentShowMode = mode;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取密码显示模式
|
||||
*/
|
||||
public int getContentShowMode() {
|
||||
return mContentShowMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入框样式
|
||||
*/
|
||||
public void setInputBoxStyle(int inputBoxStyle) {
|
||||
if (inputBoxStyle != INPUT_BOX_STYLE_CONNECT
|
||||
&& inputBoxStyle != INPUT_BOX_STYLE_SINGLE
|
||||
&& inputBoxStyle != INPUT_BOX_STYLE_UNDERLINE
|
||||
) {
|
||||
throw new IllegalArgumentException(
|
||||
"the value of the parameter must be one of" +
|
||||
"{1:INPUT_BOX_STYLE_CONNECT}, " +
|
||||
"{2:INPUT_BOX_STYLE_SINGLE} or " +
|
||||
"{3:INPUT_BOX_STYLE_UNDERLINE}"
|
||||
);
|
||||
}
|
||||
mInputBoxStyle = inputBoxStyle;
|
||||
// 这里没有调用invalidate因为会存在问题
|
||||
// invalidate会重绘,但是不会去重新测量,当输入框样式切换的之后,item的宽度其实是有变化的,所以此时需要重新测量
|
||||
// requestLayout,调用onMeasure和onLayout,不一定会调用onDraw,当view的l,t,r,b发生改变时会调用onDraw
|
||||
requestLayout();
|
||||
//invalidate();
|
||||
}
|
||||
|
||||
public void setOnInputListener(OnInputListener listener) {
|
||||
this.inputListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过复写onTextChanged来实现对输入的监听
|
||||
* 如果在onDraw里面监听text的输入长度来实现,会重复的调用该方法,就不妥当
|
||||
*/
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
|
||||
super.onTextChanged(text, start, lengthBefore, lengthAfter);
|
||||
String content = text.toString().trim();
|
||||
if (inputListener != null) {
|
||||
inputListener.onInputChanged(content);
|
||||
if (content.length() == mContentNumber) {
|
||||
inputListener.onInputFinished(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入框样式
|
||||
*/
|
||||
public int getInputBoxStyle() {
|
||||
return mInputBoxStyle;
|
||||
}
|
||||
|
||||
private float dp2px(float dpValue) {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dpValue,
|
||||
Resources.getSystem().getDisplayMetrics()
|
||||
);
|
||||
}
|
||||
|
||||
private float sp2px(float spValue) {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
spValue,
|
||||
Resources.getSystem().getDisplayMetrics()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 光标Runnable
|
||||
* 通过Runnable每500ms执行重绘,每次runnable通过改变画笔的alpha值来使光标产生闪烁的效果
|
||||
*/
|
||||
private class CursorRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//获取光标画笔的alpha值
|
||||
int alpha = mPaintCursor.getAlpha();
|
||||
//设置光标画笔的alpha值
|
||||
mPaintCursor.setAlpha(alpha == 0 ? 255 : 0);
|
||||
invalidate();
|
||||
postDelayed(this, mCursorDuration);
|
||||
// L.e("SplitEditText","CursorRunnable-----run-->");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 输入的监听抽象类
|
||||
* 没定义接口的原因是可以在抽象类里面定义空实现的方法,可以让用户根据需求选择性的复写某些方法
|
||||
*/
|
||||
public interface OnInputListener {
|
||||
|
||||
/**
|
||||
* 输入完成的抽象方法
|
||||
*
|
||||
* @param content 输入内容
|
||||
*/
|
||||
public abstract void onInputFinished(String content);
|
||||
|
||||
/**
|
||||
* 输入的内容
|
||||
*/
|
||||
public void onInputChanged(String text) ;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.TypedValue;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static <T> T checkNotNull(T object, String message) {
|
||||
if (object == null) {
|
||||
throw new NullPointerException(message);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
public static int pixelOfScaled(Context c, int sp) {
|
||||
Resources r = c.getResources();
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, r.getDisplayMetrics());
|
||||
}
|
||||
|
||||
public static int pixelOfDp(Context c, int dp) {
|
||||
Resources r = c.getResources();
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
|
||||
}
|
||||
|
||||
|
||||
private static final Object sLock = new Object();
|
||||
private static TypedValue sTempValue;
|
||||
/**
|
||||
* Returns a drawable object associated with a particular resource ID.
|
||||
* <p>
|
||||
* Starting in {@link Build.VERSION_CODES#LOLLIPOP}, the
|
||||
* returned drawable will be styled for the specified Context's theme.
|
||||
*
|
||||
* @param id The desired resource identifier, as generated by the aapt tool.
|
||||
* This integer encodes the package, type, and resource entry.
|
||||
* The value 0 is an invalid identifier.
|
||||
* @return Drawable An object that can be used to draw this resource.
|
||||
*/
|
||||
public static Drawable getDrawable(Context context, int id) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return context.getDrawable(id);
|
||||
} else if (Build.VERSION.SDK_INT >= 16) {
|
||||
//noinspection deprecation
|
||||
return context.getResources().getDrawable(id);
|
||||
} else {
|
||||
// Prior to JELLY_BEAN, Resources.getDrawable() would not correctly
|
||||
// retrieve the final configuration density when the resource ID
|
||||
// is a reference another Drawable resource. As a workaround, try
|
||||
// to resolve the drawable reference manually.
|
||||
final int resolvedId;
|
||||
synchronized (sLock) {
|
||||
if (sTempValue == null) {
|
||||
sTempValue = new TypedValue();
|
||||
}
|
||||
context.getResources().getValue(id, sTempValue, true);
|
||||
resolvedId = sTempValue.resourceId;
|
||||
}
|
||||
//noinspection deprecation
|
||||
return context.getResources().getDrawable(resolvedId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.petterp.floatingx.listener.control.IFxControl;
|
||||
import com.tencent.liteav.base.Log;
|
||||
|
||||
public class ViewUtils {
|
||||
public static void waitUntilViewReady(@NonNull IFxControl control, @NonNull OnViewCreatedListener listener) {
|
||||
final int[] retryCount = {0};
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
final Runnable checkRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
View view = control.getView();
|
||||
if (view != null) {
|
||||
listener.onViewCreated(view);
|
||||
} else if (retryCount[0]++ < 10) { // 最多尝试 10 次
|
||||
handler.postDelayed(this, 300); // 每隔 300ms 再试一次
|
||||
} else {
|
||||
Log.e("ViewUtils", "等待 View 创建超时");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handler.post(checkRunnable);
|
||||
}
|
||||
|
||||
public interface OnViewCreatedListener {
|
||||
void onViewCreated(@NonNull View view);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.blankj.utilcode.util.ConvertUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.room.RoomUserJoinModel;
|
||||
import com.xscm.moduleutil.databinding.RoomViewWelcomeAnimViewBinding;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
*@author qx
|
||||
*@data 2025/6/9
|
||||
*@description: 进场公屏
|
||||
*/
|
||||
public class WelcomeAnimView extends ConstraintLayout implements Animation.AnimationListener {
|
||||
|
||||
|
||||
private Animation mAnimation;
|
||||
public boolean animEnded = true;
|
||||
private RoomViewWelcomeAnimViewBinding mBinding;
|
||||
|
||||
public WelcomeAnimView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public WelcomeAnimView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_view_welcome_anim_view, this, true);
|
||||
setVisibility(GONE);
|
||||
mAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.room_anim_set_welcome);
|
||||
mAnimation.setFillAfter(true);
|
||||
mAnimation.setAnimationListener(this);
|
||||
}
|
||||
|
||||
public void showAnim() {
|
||||
if (SpUtil.getOpenEffect() == 0) {
|
||||
return;
|
||||
}
|
||||
if (queue == null || queue.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (animEnded) {
|
||||
RoomUserJoinModel animBean = queue.poll();
|
||||
if (animBean != null) {
|
||||
// mIvRole.setRole(animBean.getRole());
|
||||
// mIvNew.setVisibility(animBean.getUser_is_new() == 1 ? VISIBLE : GONE);
|
||||
mBinding.tvName.setText(animBean.getNickname());
|
||||
// mIvNobility.setVisibility(TextUtils.isEmpty(animBean.getNobility_icon()) ? GONE : VISIBLE);
|
||||
// ImageUtils.loadImageView(animBean.getNobility_icon(), mIvNobility);
|
||||
mBinding.ivRank.setVisibility(TextUtils.isEmpty(animBean.getRank_icon()) ? GONE : VISIBLE);
|
||||
ImageUtils.loadImageView(animBean.getRank_icon(), mBinding.ivRank);
|
||||
}
|
||||
setVisibility(VISIBLE);
|
||||
//加入房间背景 和 字体颜色
|
||||
LayoutParams infoParams = (LayoutParams) mBinding.llInfo.getLayoutParams();
|
||||
if (!TextUtils.isEmpty(animBean.getBackground())) {
|
||||
mBinding.ivUserInto.setVisibility(VISIBLE);
|
||||
mBinding.llInfo.setBackgroundResource(R.drawable.bg_r10_transparent);
|
||||
infoParams.leftMargin = ConvertUtils.dp2px(74);
|
||||
ImageUtils.loadImageView(animBean.getBackground(), mBinding.ivUserInto);
|
||||
} else {
|
||||
infoParams.leftMargin = 0;
|
||||
mBinding.llInfo.setBackgroundResource(R.drawable.room_bg_welcome_anim);
|
||||
mBinding.ivUserInto.setVisibility(INVISIBLE);
|
||||
}
|
||||
mBinding.llInfo.setLayoutParams(infoParams);
|
||||
if (!TextUtils.isEmpty(animBean.getColor())) {
|
||||
try {
|
||||
mBinding.tvEndTxt.setTextColor(Color.parseColor(animBean.getColor()));
|
||||
mBinding.tvName.setTextColor(Color.parseColor(animBean.getColor()));
|
||||
} catch (Exception e) {
|
||||
mBinding.tvEndTxt.setTextColor(Color.WHITE);
|
||||
mBinding.tvName.setTextColor(Color.WHITE);
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
mBinding.tvEndTxt.setTextColor(Color.WHITE);
|
||||
mBinding.tvName.setTextColor(Color.WHITE);
|
||||
}
|
||||
this.startAnimation(mAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAnim(RoomUserJoinModel bean) {
|
||||
if (SpUtil.getOpenEffect() == 0) {
|
||||
return;
|
||||
}
|
||||
if (queue.size() == 0) {
|
||||
queue.add(bean);
|
||||
showAnim();
|
||||
} else {
|
||||
queue.add(bean);
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<RoomUserJoinModel> queue = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
animEnded = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
animEnded = true;
|
||||
showAnim();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭特效
|
||||
*/
|
||||
public void closeEffect() {
|
||||
//清空队列
|
||||
queue.clear();
|
||||
//关闭动画
|
||||
if (mAnimation != null && !animEnded) {
|
||||
animEnded = true;
|
||||
mAnimation.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.databinding.RoomViewWheatCharmBinding;
|
||||
|
||||
|
||||
public class WheatCharmView extends RelativeLayout {
|
||||
|
||||
|
||||
private RoomViewWheatCharmBinding mBinding;
|
||||
|
||||
public WheatCharmView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public WheatCharmView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_view_wheat_charm, this, true);
|
||||
setClipChildren(false);
|
||||
setClipToPadding(false);
|
||||
}
|
||||
|
||||
public void setSex(String sex, String userId, String value, boolean format) {
|
||||
if (format) {
|
||||
mBinding.tvValue.setText(value);
|
||||
} else {
|
||||
try {
|
||||
long xd = Long.parseLong(value);
|
||||
if (xd > 9999 || xd < -9999) {
|
||||
mBinding.tvValue.setText(String.format("%.2fw", xd / 10000.0f));
|
||||
// mBinding.tvValue.setText(String.valueOf(xd));
|
||||
} else {
|
||||
mBinding.tvValue.setText(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setBg(int bg){
|
||||
mBinding.bg.setBackgroundResource(bg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.blankj.utilcode.util.ScreenUtils;
|
||||
import com.xscm.moduleutil.utils.GiftAnimatorUtil;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
public class WheatGiftAnim {
|
||||
|
||||
public static void addGift(View targetView, String imageUrl) {
|
||||
final int[] location = new int[2];
|
||||
targetView.getLocationOnScreen(location);
|
||||
ImageView imageView = new ImageView(targetView.getContext());
|
||||
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(targetView.getWidth(), targetView.getHeight());
|
||||
imageView.setLayoutParams(params);
|
||||
ImageUtils.loadImageView(imageUrl, imageView);
|
||||
attach2Activity((Activity) targetView.getContext(), imageView);
|
||||
ObjectAnimator tada = GiftAnimatorUtil.tada(imageView);
|
||||
PropertyValuesHolder objectAnimatorY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, ScreenUtils.getScreenHeight(), location[1]);
|
||||
PropertyValuesHolder objectAnimatorX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, ScreenUtils.getScreenWidth() / 2f, location[0]);
|
||||
ObjectAnimator valueAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, objectAnimatorY, objectAnimatorX).
|
||||
setDuration(1200);
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
set.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
removeFromActivity(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
removeFromActivity(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animator) {
|
||||
|
||||
}
|
||||
});
|
||||
set.playSequentially(valueAnimator, tada);
|
||||
set.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将创建的ExplosionField添加到Activity上
|
||||
*/
|
||||
private static void attach2Activity(Activity activity, View view) {
|
||||
ViewGroup rootView = activity.findViewById(Window.ID_ANDROID_CONTENT);
|
||||
rootView.addView(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将ExplosionField从Activity上移除
|
||||
*
|
||||
* @param imageView
|
||||
*/
|
||||
private static void removeFromActivity(ImageView imageView) {
|
||||
ViewGroup rootView = ((Activity) imageView.getContext()).findViewById(Window.ID_ANDROID_CONTENT);
|
||||
rootView.removeView(imageView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.UserOnlineStatusBean;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WheatLayoutManager {
|
||||
private final Context context;
|
||||
private final ViewGroup container;
|
||||
private List<RoomPitBean> pitList;
|
||||
private boolean isSingleMode = false;
|
||||
private int currentSinglePit = -1;
|
||||
private RoomDefaultWheatView singleWheatView;
|
||||
|
||||
private final int[] pitIndexMap = {9, 10, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
public interface OnWheatClickListener {
|
||||
void onWheatClick(RoomDefaultWheatView view, int pitNumber);
|
||||
|
||||
void onMakeWheatClick(RoomDefaultWheatView view, int pitNumber);
|
||||
|
||||
}
|
||||
|
||||
private @Nullable OnWheatClickListener wheatClickListener;
|
||||
|
||||
public WheatLayoutManager(Context context, ViewGroup container) {
|
||||
this.context = context;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public void setWheatData(List<RoomPitBean> pitList) {
|
||||
this.pitList = pitList;
|
||||
restoreMultiWheat();
|
||||
}
|
||||
|
||||
public void setWheatDataPk(List<RoomPitBean> pitList, int layoutType) {
|
||||
this.pitList = pitList;
|
||||
restoreMultiWheatPk(layoutType);
|
||||
}
|
||||
|
||||
public void setOnWheatClickListener(@Nullable OnWheatClickListener listener) {
|
||||
this.wheatClickListener = listener;
|
||||
}
|
||||
|
||||
public void showSingleWheat(int pitNumber) {
|
||||
if (isSingleMode && this.currentSinglePit == pitNumber) return;
|
||||
|
||||
container.removeAllViews();
|
||||
|
||||
if (pitNumber < 1 || pitNumber > 10 || pitList == null || pitList.size() < 10)
|
||||
return;
|
||||
|
||||
RoomPitBean bean = pitList.get(pitNumber - 1);
|
||||
|
||||
singleWheatView = new RoomDefaultWheatView(context);
|
||||
singleWheatView.pitNumber = String.valueOf(pitNumber);
|
||||
singleWheatView.setData(bean);
|
||||
|
||||
// 默认设置为 MATCH_PARENT,也可以自定义
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
params.setMargins(20, 20, 20, 20);
|
||||
singleWheatView.setLayoutParams(params);
|
||||
|
||||
// 添加点击事件
|
||||
singleWheatView.setOnClickListener(v -> {
|
||||
if (wheatClickListener != null) {
|
||||
wheatClickListener.onWheatClick(singleWheatView, pitNumber);
|
||||
}
|
||||
restoreMultiWheat();
|
||||
});
|
||||
|
||||
container.addView(singleWheatView);
|
||||
isSingleMode = true;
|
||||
currentSinglePit = pitNumber;
|
||||
}
|
||||
|
||||
public void restoreMultiWheat() {
|
||||
container.removeAllViews();
|
||||
|
||||
int screenWidth = getScreenWidth();
|
||||
int itemWidth = screenWidth / 4; // 每个控件宽度为屏幕宽度的 1/4
|
||||
|
||||
LinearLayout row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int pitNumber = pitIndexMap[i];
|
||||
RoomDefaultWheatView wheatView = new RoomDefaultWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
|
||||
LinearLayout.LayoutParams params;
|
||||
|
||||
if (i == 0) {
|
||||
int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
int fixedHeightInPx = dpToPx(fixedHeightInDp); // 调用已有的 dpToPx 方法
|
||||
// 第一个控件:左边距 86dp,右边距 100dp
|
||||
params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
params.rightMargin = dpToPx(50);
|
||||
} else if (i == 1) {
|
||||
int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
int fixedHeightInPx = dpToPx(fixedHeightInDp); // 调用已有的 dpToPx 方法
|
||||
// 第二个控件:右边距 86dp
|
||||
params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
} else {
|
||||
int fixedHeightInDp = 90; // 固定高度为 100dp
|
||||
int fixedHeightInPx = dpToPx(fixedHeightInDp); // 调用已有的 dpToPx 方法
|
||||
params = new LinearLayout.LayoutParams(itemWidth - 30, fixedHeightInPx + 30);
|
||||
// 其他控件保持原有逻辑
|
||||
|
||||
|
||||
// if (i > 1 && (i - 2) % 4 != 0) {
|
||||
// params.leftMargin = 12;
|
||||
// params.rightMargin = 12;
|
||||
// }
|
||||
params.setMargins(0, 0, 0, 0); // 不设右边距,由 row padding 控制
|
||||
}
|
||||
|
||||
wheatView.setLayoutParams(params);
|
||||
wheatView.setOnClickListener(v -> {
|
||||
if (wheatClickListener != null) {
|
||||
wheatClickListener.onWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
}
|
||||
// showSingleWheat(Integer.parseInt(wheatView.pitNumber));
|
||||
});
|
||||
|
||||
row.addView(wheatView);
|
||||
|
||||
// 第一行添加两个后换行
|
||||
if (i == 1) {
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
} else if (i > 1 && (i - 2) % 4 == 3) {
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一行可能存在的剩余 view
|
||||
if (row.getChildCount() > 0) {
|
||||
container.addView(row);
|
||||
}
|
||||
isSingleMode = false;
|
||||
currentSinglePit = -1;
|
||||
|
||||
}
|
||||
// public void restoreMultiWheatPk(int layoutType, int width) {
|
||||
// container.removeAllViews();
|
||||
//
|
||||
// int screenWidth = getScreenWidth();
|
||||
// int itemWidth = screenWidth / 8; // 每个控件宽度为屏幕宽度的 1/4
|
||||
//
|
||||
// LinearLayout row = new LinearLayout(context);
|
||||
// row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
//
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// int pitNumber = pitIndexMap[i];
|
||||
// RoomDefaultWheatView wheatView = new RoomDefaultWheatView(context);
|
||||
// wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
// wheatView.setData(pitList.get(pitNumber - 1));
|
||||
//
|
||||
// LinearLayout.LayoutParams params;
|
||||
//
|
||||
// if (i == 0) {
|
||||
// int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
// int fixedHeightInPx =context.getResources().getDimensionPixelSize(R.dimen.dp_80); // 调用已有的 dpToPx 方法
|
||||
// // 第一个控件:左边距 86dp,右边距 100dp
|
||||
// params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
// params.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_50);
|
||||
// } else if (i == 1) {
|
||||
// int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
// int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_80); // 调用已有的 dpToPx 方法
|
||||
// // 第二个控件:右边距 86dp
|
||||
// params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
// } else {
|
||||
// int fixedHeightInDp = 90; // 固定高度为 100dp
|
||||
// int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_60); // 调用已有的 dpToPx 方法
|
||||
// params = new LinearLayout.LayoutParams(itemWidth -10, fixedHeightInPx + 30);
|
||||
// // 其他控件保持原有逻辑
|
||||
//
|
||||
//
|
||||
//// if (i > 1 && (i - 2) % 4 != 0) {
|
||||
//// params.leftMargin = 12;
|
||||
//// params.rightMargin = 12;
|
||||
//// }
|
||||
// params.setMargins(0, 0, 0, 0); // 不设右边距,由 row padding 控制
|
||||
// }
|
||||
//
|
||||
// wheatView.setLayoutParams(params);
|
||||
// wheatView.setOnClickListener(v -> {
|
||||
// if (wheatClickListener != null) {
|
||||
// wheatClickListener.onWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
// }
|
||||
//// showSingleWheat(Integer.parseInt(wheatView.pitNumber));
|
||||
// });
|
||||
//
|
||||
// row.addView(wheatView);
|
||||
//
|
||||
// // 第一行添加两个后换行
|
||||
// if (i == 1) {
|
||||
// container.addView(row);
|
||||
// row = new LinearLayout(context);
|
||||
// row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
// } else if (i > 1 && (i - 2) % 4 == 3) {
|
||||
// container.addView(row);
|
||||
// row = new LinearLayout(context);
|
||||
// row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//// 添加最后一行可能存在的剩余 view
|
||||
// if (row.getChildCount() > 0) {
|
||||
// container.addView(row);
|
||||
// }
|
||||
// isSingleMode = false;
|
||||
// currentSinglePit = -1;
|
||||
// }
|
||||
|
||||
|
||||
public void restoreMultiWheatPk(int layoutType) {
|
||||
if (layoutType == 1) {
|
||||
container.removeAllViews();
|
||||
}
|
||||
int screenWidth = getScreenWidth();
|
||||
int itemWidth = screenWidth / 8;
|
||||
|
||||
LinearLayout row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
// 根据 layoutType 调整前两个控件的顺序
|
||||
int firstPitNumber, secondPitNumber;
|
||||
if (layoutType == 1) {
|
||||
firstPitNumber = 10; // 第一个显示 10
|
||||
secondPitNumber = 9; // 第二个显示 9
|
||||
} else if (layoutType == 2) {
|
||||
firstPitNumber = 9; // 第一个显示 9
|
||||
secondPitNumber = 10; // 第二个显示 10
|
||||
} else {
|
||||
firstPitNumber = 9;
|
||||
secondPitNumber = 10;
|
||||
}
|
||||
|
||||
// 添加第一个控件(10 或 9)
|
||||
addWheatViewItem(row, firstPitNumber, itemWidth*2, layoutType);
|
||||
|
||||
// 添加第二个控件(9 或 10)
|
||||
addWheatViewItem(row, secondPitNumber, itemWidth*2, layoutType);
|
||||
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
// 添加其余 8 个控件(1~8)
|
||||
for (int i = 2; i < 10; i++) {
|
||||
int pitNumber = pitIndexMap[i];
|
||||
addWheatViewItem(row, pitNumber, itemWidth, layoutType);
|
||||
|
||||
if (i > 1 && (i - 2) % 4 == 3) {
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (row.getChildCount() > 0) {
|
||||
container.addView(row);
|
||||
}
|
||||
|
||||
isSingleMode = false;
|
||||
currentSinglePit = -1;
|
||||
}
|
||||
|
||||
// 抽取公共方法:添加单个控件
|
||||
private void addWheatViewItem(LinearLayout row, int pitNumber, int itemWidth, int layoutType) {
|
||||
RoomDefaultWheatView wheatView = new RoomDefaultWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
|
||||
LinearLayout.LayoutParams params;
|
||||
|
||||
if (pitNumber == 9 || pitNumber == 10) {
|
||||
int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_90);
|
||||
|
||||
if (pitNumber == 9) {
|
||||
params = new LinearLayout.LayoutParams(itemWidth-40, fixedHeightInPx);
|
||||
if (layoutType == 1) {
|
||||
// 9号在右边,右边距10dp
|
||||
params.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_1);
|
||||
params.setMargins(20, -30, -20, 0);
|
||||
} else if (layoutType == 2) {
|
||||
// 9号在左边,左边距10dp
|
||||
params.leftMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_1);
|
||||
params.setMargins(-30, -20, 0, 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
params = new LinearLayout.LayoutParams(itemWidth - 80, fixedHeightInPx);
|
||||
if (layoutType == 1) {
|
||||
// 10号在左边,左边距15dp
|
||||
// params.leftMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_5);
|
||||
params.setMargins(-30, 10, 0, 0);
|
||||
} else if (layoutType == 2) {
|
||||
// 10号在右边,右边距15dp
|
||||
// params.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_5);
|
||||
params.setMargins(0, 10, -30, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_60);
|
||||
params = new LinearLayout.LayoutParams(itemWidth + 15, fixedHeightInPx + 20);
|
||||
params.setMargins(-20, -20, -20, 0);
|
||||
}
|
||||
|
||||
wheatView.setLayoutParams(params);
|
||||
wheatView.setOnClickListener(v -> {
|
||||
if (wheatClickListener != null) {
|
||||
if (layoutType==1) {
|
||||
wheatClickListener.onWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
}else {
|
||||
wheatClickListener.onMakeWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
row.addView(wheatView);
|
||||
}
|
||||
|
||||
|
||||
private RoomDefaultWheatView createWheatView(int pitNumber) {
|
||||
RoomDefaultWheatView wheatView = new RoomDefaultWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
return wheatView;
|
||||
}
|
||||
|
||||
private RoomMakeWheatView createRoomMakeWheatView(int pitNumber) {
|
||||
RoomMakeWheatView wheatView = new RoomMakeWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
return wheatView;
|
||||
}
|
||||
|
||||
|
||||
private int dpToPx(int dp) {
|
||||
return Math.round(dp * context.getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
private int getScreenWidth() {
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
return metrics.widthPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定 pitNumber 的麦位信息(用于局部刷新)
|
||||
*/
|
||||
public void updateSingleWheat(RoomPitBean pitBean, int pitNumber) {
|
||||
if (pitList == null || pitList.isEmpty() || pitNumber < 1 || pitNumber > 10) return;
|
||||
|
||||
// 如果是单个展示模式且不是当前麦位,不处理
|
||||
if (isSingleMode && this.currentSinglePit != pitNumber) return;
|
||||
|
||||
RoomDefaultWheatView wheatView = findWheatViewByPitNumber(pitNumber);
|
||||
if (wheatView != null) {
|
||||
|
||||
// RoomPitBean bean = pitList.get(pitNumber - 1);
|
||||
RoomPitBean bean = pitBean;
|
||||
wheatView.setData(bean); // 刷新数据
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RoomDefaultWheatView findWheatViewByPitNumber(int pitNumber) {
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
View row = container.getChildAt(i);
|
||||
if (row instanceof LinearLayout) {
|
||||
LinearLayout linearRow = (LinearLayout) row;
|
||||
for (int j = 0; j < linearRow.getChildCount(); j++) {
|
||||
View child = linearRow.getChildAt(j);
|
||||
if (child instanceof RoomDefaultWheatView) {
|
||||
RoomDefaultWheatView view = (RoomDefaultWheatView) child;
|
||||
if (Integer.parseInt(view.pitNumber) == pitNumber) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (row instanceof RoomDefaultWheatView) {
|
||||
RoomDefaultWheatView view = (RoomDefaultWheatView) row;
|
||||
if (Integer.parseInt(view.pitNumber) == pitNumber) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量刷新多个麦位状态
|
||||
*/
|
||||
public void refreshWheatData(List<RoomPitBean> newPitList, List<Integer> changedPits) {
|
||||
this.pitList = newPitList;
|
||||
for (int pitNumber : changedPits) {
|
||||
// updateSingleWheat(pitNumber);
|
||||
}
|
||||
}
|
||||
public void updateSingleOnlineWheat(UserOnlineStatusBean bean) {
|
||||
if (pitList == null || pitList.isEmpty()) return;
|
||||
|
||||
for (RoomPitBean pitBean : pitList) {
|
||||
int pitNumber = Integer.parseInt(pitBean.getPit_number());
|
||||
RoomDefaultWheatView wheatView = findWheatViewByPitNumber(pitNumber);
|
||||
wheatView.setOnlineStatus(bean); // 刷新数据
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.UserOnlineStatusBean;
|
||||
import com.xscm.moduleutil.bean.room.RoomPitBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author lxj$
|
||||
* @Time 2025-8-6 17:29:04$ $
|
||||
* @Description 二卡八显示布局$
|
||||
*/
|
||||
public class WheatLayoutSingManager {
|
||||
private final Context context;
|
||||
private final ViewGroup container;
|
||||
private List<RoomPitBean> pitList;
|
||||
private boolean isSingleMode = false;
|
||||
private int currentSinglePit = -1;
|
||||
private RoomSingSongWheatView singleWheatView;
|
||||
|
||||
private final int[] pitIndexMap = {9, 10, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
public interface OnWheatClickListener {
|
||||
void onWheatClick(RoomSingSongWheatView view, int pitNumber);
|
||||
|
||||
void onMakeWheatClick(RoomSingSongWheatView view, int pitNumber);
|
||||
|
||||
}
|
||||
|
||||
private @Nullable OnWheatClickListener wheatClickListener;
|
||||
|
||||
public WheatLayoutSingManager(Context context, ViewGroup container) {
|
||||
this.context = context;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public void setWheatData(List<RoomPitBean> pitList) {
|
||||
this.pitList = pitList;
|
||||
restoreMultiWheat();
|
||||
}
|
||||
|
||||
public void setWheatDataPk(List<RoomPitBean> pitList, int layoutType) {
|
||||
this.pitList = pitList;
|
||||
restoreMultiWheatPk(layoutType);
|
||||
}
|
||||
|
||||
public void setOnWheatClickListener(@Nullable OnWheatClickListener listener) {
|
||||
this.wheatClickListener = listener;
|
||||
}
|
||||
|
||||
public void showSingleWheat(int pitNumber) {
|
||||
if (isSingleMode && this.currentSinglePit == pitNumber) return;
|
||||
|
||||
container.removeAllViews();
|
||||
|
||||
if (pitNumber < 1 || pitNumber > 10 || pitList == null || pitList.size() < 10)
|
||||
return;
|
||||
|
||||
RoomPitBean bean = pitList.get(pitNumber - 1);
|
||||
|
||||
singleWheatView = new RoomSingSongWheatView(context);
|
||||
singleWheatView.pitNumber = String.valueOf(pitNumber);
|
||||
singleWheatView.setData(bean);
|
||||
|
||||
// 默认设置为 MATCH_PARENT,也可以自定义
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
params.setMargins(20, 20, 20, 20);
|
||||
singleWheatView.setLayoutParams(params);
|
||||
|
||||
// 添加点击事件
|
||||
singleWheatView.setOnClickListener(v -> {
|
||||
if (wheatClickListener != null) {
|
||||
wheatClickListener.onWheatClick(singleWheatView, pitNumber);
|
||||
}
|
||||
restoreMultiWheat();
|
||||
});
|
||||
|
||||
container.addView(singleWheatView);
|
||||
isSingleMode = true;
|
||||
currentSinglePit = pitNumber;
|
||||
}
|
||||
|
||||
public void restoreMultiWheat() {
|
||||
container.removeAllViews();
|
||||
|
||||
int screenWidth = getScreenWidth();
|
||||
int itemWidth = screenWidth / 4; // 每个控件宽度为屏幕宽度的 1/4
|
||||
|
||||
LinearLayout row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int pitNumber = pitIndexMap[i];
|
||||
RoomSingSongWheatView wheatView = new RoomSingSongWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
|
||||
LinearLayout.LayoutParams params;
|
||||
|
||||
if (i == 0) {
|
||||
int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
int fixedHeightInPx = dpToPx(fixedHeightInDp); // 调用已有的 dpToPx 方法
|
||||
// 第一个控件:左边距 86dp,右边距 100dp
|
||||
params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
params.rightMargin = dpToPx(50);
|
||||
} else if (i == 1) {
|
||||
int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
int fixedHeightInPx = dpToPx(fixedHeightInDp); // 调用已有的 dpToPx 方法
|
||||
// 第二个控件:右边距 86dp
|
||||
params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
} else {
|
||||
int fixedHeightInDp = 90; // 固定高度为 100dp
|
||||
int fixedHeightInPx = dpToPx(fixedHeightInDp); // 调用已有的 dpToPx 方法
|
||||
params = new LinearLayout.LayoutParams(itemWidth - 30, fixedHeightInPx + 30);
|
||||
// 其他控件保持原有逻辑
|
||||
|
||||
|
||||
// if (i > 1 && (i - 2) % 4 != 0) {
|
||||
// params.leftMargin = 12;
|
||||
// params.rightMargin = 12;
|
||||
// }
|
||||
params.setMargins(0, 0, 0, 0); // 不设右边距,由 row padding 控制
|
||||
}
|
||||
|
||||
wheatView.setLayoutParams(params);
|
||||
wheatView.setOnClickListener(v -> {
|
||||
if (wheatClickListener != null) {
|
||||
wheatClickListener.onWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
}
|
||||
// showSingleWheat(Integer.parseInt(wheatView.pitNumber));
|
||||
});
|
||||
|
||||
row.addView(wheatView);
|
||||
|
||||
// 第一行添加两个后换行
|
||||
if (i == 1) {
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
} else if (i > 1 && (i - 2) % 4 == 3) {
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一行可能存在的剩余 view
|
||||
if (row.getChildCount() > 0) {
|
||||
container.addView(row);
|
||||
}
|
||||
isSingleMode = false;
|
||||
currentSinglePit = -1;
|
||||
|
||||
}
|
||||
// public void restoreMultiWheatPk(int layoutType, int width) {
|
||||
// container.removeAllViews();
|
||||
//
|
||||
// int screenWidth = getScreenWidth();
|
||||
// int itemWidth = screenWidth / 8; // 每个控件宽度为屏幕宽度的 1/4
|
||||
//
|
||||
// LinearLayout row = new LinearLayout(context);
|
||||
// row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
//
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// int pitNumber = pitIndexMap[i];
|
||||
// RoomDefaultWheatView wheatView = new RoomDefaultWheatView(context);
|
||||
// wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
// wheatView.setData(pitList.get(pitNumber - 1));
|
||||
//
|
||||
// LinearLayout.LayoutParams params;
|
||||
//
|
||||
// if (i == 0) {
|
||||
// int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
// int fixedHeightInPx =context.getResources().getDimensionPixelSize(R.dimen.dp_80); // 调用已有的 dpToPx 方法
|
||||
// // 第一个控件:左边距 86dp,右边距 100dp
|
||||
// params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
// params.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_50);
|
||||
// } else if (i == 1) {
|
||||
// int fixedHeightInDp = 110; // 固定高度为 100dp
|
||||
// int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_80); // 调用已有的 dpToPx 方法
|
||||
// // 第二个控件:右边距 86dp
|
||||
// params = new LinearLayout.LayoutParams(itemWidth, fixedHeightInPx);
|
||||
// } else {
|
||||
// int fixedHeightInDp = 90; // 固定高度为 100dp
|
||||
// int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_60); // 调用已有的 dpToPx 方法
|
||||
// params = new LinearLayout.LayoutParams(itemWidth -10, fixedHeightInPx + 30);
|
||||
// // 其他控件保持原有逻辑
|
||||
//
|
||||
//
|
||||
//// if (i > 1 && (i - 2) % 4 != 0) {
|
||||
//// params.leftMargin = 12;
|
||||
//// params.rightMargin = 12;
|
||||
//// }
|
||||
// params.setMargins(0, 0, 0, 0); // 不设右边距,由 row padding 控制
|
||||
// }
|
||||
//
|
||||
// wheatView.setLayoutParams(params);
|
||||
// wheatView.setOnClickListener(v -> {
|
||||
// if (wheatClickListener != null) {
|
||||
// wheatClickListener.onWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
// }
|
||||
//// showSingleWheat(Integer.parseInt(wheatView.pitNumber));
|
||||
// });
|
||||
//
|
||||
// row.addView(wheatView);
|
||||
//
|
||||
// // 第一行添加两个后换行
|
||||
// if (i == 1) {
|
||||
// container.addView(row);
|
||||
// row = new LinearLayout(context);
|
||||
// row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
// } else if (i > 1 && (i - 2) % 4 == 3) {
|
||||
// container.addView(row);
|
||||
// row = new LinearLayout(context);
|
||||
// row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//// 添加最后一行可能存在的剩余 view
|
||||
// if (row.getChildCount() > 0) {
|
||||
// container.addView(row);
|
||||
// }
|
||||
// isSingleMode = false;
|
||||
// currentSinglePit = -1;
|
||||
// }
|
||||
|
||||
|
||||
public void restoreMultiWheatPk(int layoutType) {
|
||||
if (layoutType == 1) {
|
||||
container.removeAllViews();
|
||||
}
|
||||
int screenWidth = getScreenWidth();
|
||||
int itemWidth = screenWidth / 8;
|
||||
|
||||
LinearLayout row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
// 根据 layoutType 调整前两个控件的顺序
|
||||
int firstPitNumber, secondPitNumber;
|
||||
if (layoutType == 1) {
|
||||
firstPitNumber = 10; // 第一个显示 10
|
||||
secondPitNumber = 9; // 第二个显示 9
|
||||
} else if (layoutType == 2) {
|
||||
firstPitNumber = 9; // 第一个显示 9
|
||||
secondPitNumber = 10; // 第二个显示 10
|
||||
} else {
|
||||
firstPitNumber = 9;
|
||||
secondPitNumber = 10;
|
||||
}
|
||||
|
||||
// 添加第一个控件(10 或 9)
|
||||
addWheatViewItem(row, firstPitNumber, itemWidth * 2, layoutType);
|
||||
|
||||
// 添加第二个控件(9 或 10)
|
||||
addWheatViewItem(row, secondPitNumber, itemWidth * 2, layoutType);
|
||||
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
// 添加其余 8 个控件(1~8)
|
||||
for (int i = 2; i < 10; i++) {
|
||||
int pitNumber = pitIndexMap[i];
|
||||
addWheatViewItem(row, pitNumber, itemWidth, layoutType);
|
||||
|
||||
if (i > 1 && (i - 2) % 4 == 3) {
|
||||
container.addView(row);
|
||||
row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (row.getChildCount() > 0) {
|
||||
container.addView(row);
|
||||
}
|
||||
|
||||
isSingleMode = false;
|
||||
currentSinglePit = -1;
|
||||
}
|
||||
|
||||
// 抽取公共方法:添加单个控件
|
||||
private void addWheatViewItem(LinearLayout row, int pitNumber, int itemWidth, int layoutType) {
|
||||
RoomSingSongWheatView wheatView = new RoomSingSongWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
|
||||
LinearLayout.LayoutParams params;
|
||||
|
||||
if (pitNumber == 9 || pitNumber == 10) {
|
||||
int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_90);
|
||||
|
||||
if (pitNumber == 9) {
|
||||
params = new LinearLayout.LayoutParams(itemWidth - 40, fixedHeightInPx);
|
||||
if (layoutType == 1) {
|
||||
// 9号在右边,右边距10dp
|
||||
params.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_1);
|
||||
params.setMargins(20, -30, -20, 0);
|
||||
} else if (layoutType == 2) {
|
||||
// 9号在左边,左边距10dp
|
||||
params.leftMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_1);
|
||||
params.setMargins(-30, -20, 0, 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
params = new LinearLayout.LayoutParams(itemWidth - 80, fixedHeightInPx);
|
||||
if (layoutType == 1) {
|
||||
// 10号在左边,左边距15dp
|
||||
// params.leftMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_5);
|
||||
params.setMargins(-30, 10, 0, 0);
|
||||
} else if (layoutType == 2) {
|
||||
// 10号在右边,右边距15dp
|
||||
// params.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_5);
|
||||
params.setMargins(0, 10, -30, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
int fixedHeightInPx = context.getResources().getDimensionPixelSize(R.dimen.dp_60);
|
||||
params = new LinearLayout.LayoutParams(itemWidth + 15, fixedHeightInPx + 20);
|
||||
params.setMargins(-20, -20, -20, 0);
|
||||
}
|
||||
|
||||
wheatView.setLayoutParams(params);
|
||||
wheatView.setOnClickListener(v -> {
|
||||
if (wheatClickListener != null) {
|
||||
if (layoutType == 1) {
|
||||
wheatClickListener.onWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
} else {
|
||||
wheatClickListener.onMakeWheatClick(wheatView, Integer.parseInt(wheatView.pitNumber));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
row.addView(wheatView);
|
||||
}
|
||||
|
||||
|
||||
private RoomSingSongWheatView createWheatView(int pitNumber) {
|
||||
RoomSingSongWheatView wheatView = new RoomSingSongWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
return wheatView;
|
||||
}
|
||||
|
||||
private RoomMakeWheatView createRoomMakeWheatView(int pitNumber) {
|
||||
RoomMakeWheatView wheatView = new RoomMakeWheatView(context);
|
||||
wheatView.pitNumber = String.valueOf(pitNumber);
|
||||
wheatView.setData(pitList.get(pitNumber - 1));
|
||||
return wheatView;
|
||||
}
|
||||
|
||||
|
||||
private int dpToPx(int dp) {
|
||||
return Math.round(dp * context.getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
private int getScreenWidth() {
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
return metrics.widthPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定 pitNumber 的麦位信息(用于局部刷新)
|
||||
*/
|
||||
public void updateSingleWheat(RoomPitBean pitBean, int pitNumber) {
|
||||
if (pitList == null || pitList.isEmpty() || pitNumber < 1 || pitNumber > 10) return;
|
||||
|
||||
// 如果是单个展示模式且不是当前麦位,不处理
|
||||
if (isSingleMode && this.currentSinglePit != pitNumber) return;
|
||||
|
||||
RoomSingSongWheatView wheatView = findWheatViewByPitNumber(pitNumber);
|
||||
if (wheatView != null) {
|
||||
|
||||
// RoomPitBean bean = pitList.get(pitNumber - 1);
|
||||
RoomPitBean bean = pitBean;
|
||||
wheatView.setData(bean); // 刷新数据
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RoomSingSongWheatView findWheatViewByPitNumber(int pitNumber) {
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
View row = container.getChildAt(i);
|
||||
if (row instanceof LinearLayout) {
|
||||
LinearLayout linearRow = (LinearLayout) row;
|
||||
for (int j = 0; j < linearRow.getChildCount(); j++) {
|
||||
View child = linearRow.getChildAt(j);
|
||||
if (child instanceof RoomSingSongWheatView) {
|
||||
RoomSingSongWheatView view = (RoomSingSongWheatView) child;
|
||||
if (Integer.parseInt(view.pitNumber) == pitNumber) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (row instanceof RoomSingSongWheatView) {
|
||||
RoomSingSongWheatView view = (RoomSingSongWheatView) row;
|
||||
if (Integer.parseInt(view.pitNumber) == pitNumber) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量刷新多个麦位状态
|
||||
*/
|
||||
public void refreshWheatData(List<RoomPitBean> newPitList, List<Integer> changedPits) {
|
||||
this.pitList = newPitList;
|
||||
for (int pitNumber : changedPits) {
|
||||
// updateSingleWheat(pitNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSingleOnlineWheat(UserOnlineStatusBean bean) {
|
||||
if (pitList == null || pitList.isEmpty()) return;
|
||||
|
||||
for (RoomPitBean pitBean : pitList) {
|
||||
int pitNumber = Integer.parseInt(pitBean.getPit_number());
|
||||
RoomSingSongWheatView wheatView = findWheatViewByPitNumber(pitNumber);
|
||||
wheatView.setOnlineStatus(bean); // 刷新数据
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.NumberPicker;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class WheelDatePicker extends LinearLayout {
|
||||
|
||||
private NumberPicker yearPicker, monthPicker, dayPicker;
|
||||
private int maxDay = 31;
|
||||
|
||||
public WheelDatePicker(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public WheelDatePicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
setOrientation(HORIZONTAL);
|
||||
|
||||
yearPicker = new NumberPicker(context);
|
||||
monthPicker = new NumberPicker(context);
|
||||
dayPicker = new NumberPicker(context);
|
||||
|
||||
LayoutParams params = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
|
||||
yearPicker.setLayoutParams(params);
|
||||
monthPicker.setLayoutParams(params);
|
||||
dayPicker.setLayoutParams(params);
|
||||
|
||||
addView(yearPicker);
|
||||
addView(monthPicker);
|
||||
addView(dayPicker);
|
||||
|
||||
// 初始化年份范围
|
||||
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
|
||||
yearPicker.setMinValue(currentYear - 50);
|
||||
yearPicker.setMaxValue(currentYear + 50);
|
||||
|
||||
// 月份从 1 到 12
|
||||
monthPicker.setMinValue(1);
|
||||
monthPicker.setMaxValue(12);
|
||||
monthPicker.setOnValueChangedListener((picker, oldVal, newVal) -> updateDays());
|
||||
|
||||
// 默认天数范围
|
||||
dayPicker.setMinValue(1);
|
||||
dayPicker.setMaxValue(maxDay);
|
||||
}
|
||||
|
||||
private void updateDays() {
|
||||
int year = yearPicker.getValue();
|
||||
int month = monthPicker.getValue();
|
||||
|
||||
int daysInMonth;
|
||||
if (month == 4 || month == 6 || month == 9 || month == 11) {
|
||||
daysInMonth = 30;
|
||||
} else if (month == 2) {
|
||||
daysInMonth = isLeapYear(year) ? 29 : 28;
|
||||
} else {
|
||||
daysInMonth = 31;
|
||||
}
|
||||
|
||||
int currentDay = dayPicker.getValue();
|
||||
dayPicker.setMaxValue(daysInMonth);
|
||||
if (currentDay > daysInMonth) {
|
||||
dayPicker.setValue(daysInMonth);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLeapYear(int year) {
|
||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||
}
|
||||
|
||||
public void init(int year, int month, int day) {
|
||||
yearPicker.setValue(year);
|
||||
monthPicker.setValue(month + 1); // Calendar 是 0-based
|
||||
dayPicker.setValue(day);
|
||||
updateDays();
|
||||
}
|
||||
|
||||
public int getYear() {
|
||||
return yearPicker.getValue();
|
||||
}
|
||||
|
||||
public int getMonth() {
|
||||
return monthPicker.getValue() - 1; // 转回 Calendar 的 0-based
|
||||
}
|
||||
|
||||
public int getDay() {
|
||||
return dayPicker.getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.NumberPicker;
|
||||
|
||||
public class WheelTimePicker extends LinearLayout {
|
||||
|
||||
private NumberPicker hourPicker, minutePicker, secondPicker;
|
||||
|
||||
public WheelTimePicker(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public WheelTimePicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
setOrientation(HORIZONTAL);
|
||||
|
||||
hourPicker = new NumberPicker(context);
|
||||
minutePicker = new NumberPicker(context);
|
||||
secondPicker= new NumberPicker(context);
|
||||
|
||||
LayoutParams params = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
|
||||
hourPicker.setLayoutParams(params);
|
||||
minutePicker.setLayoutParams(params);
|
||||
secondPicker.setLayoutParams(params);
|
||||
|
||||
addView(hourPicker);
|
||||
addView(minutePicker);
|
||||
addView(secondPicker);
|
||||
|
||||
// 设置小时范围
|
||||
hourPicker.setMinValue(0);
|
||||
hourPicker.setMaxValue(23);
|
||||
|
||||
// 设置分钟范围
|
||||
minutePicker.setMinValue(0);
|
||||
minutePicker.setMaxValue(59);
|
||||
|
||||
// 秒范围
|
||||
secondPicker.setMinValue(0);
|
||||
secondPicker.setMaxValue(59);
|
||||
}
|
||||
|
||||
public void init(int hour, int minute, int second) {
|
||||
hourPicker.setValue(hour);
|
||||
minutePicker.setValue(minute);
|
||||
secondPicker.setValue(second);
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
return hourPicker.getValue();
|
||||
}
|
||||
|
||||
public int getMinute() {
|
||||
return minutePicker.getValue();
|
||||
}
|
||||
|
||||
public int getSecond() {
|
||||
return secondPicker.getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.xscm.moduleutil.widget.animator;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* 说明:爆炸动画类,让离子移动和控制离子透明度
|
||||
* 作者:Jian
|
||||
* 时间:2017/12/26.
|
||||
*/
|
||||
class ExplosionAnimator extends ValueAnimator {
|
||||
private static final int DEFAULT_DURATION = 3000;
|
||||
private ParticleModel[][] mParticles;
|
||||
private Paint mPaint;
|
||||
private View mContainer;
|
||||
|
||||
public ExplosionAnimator(View view, Bitmap bitmap, Rect bound) {
|
||||
setFloatValues(0.0f, 1.0f);
|
||||
setDuration(DEFAULT_DURATION);
|
||||
|
||||
mPaint = new Paint();
|
||||
mContainer = view;
|
||||
mParticles = generateParticles(bitmap, bound);
|
||||
}
|
||||
|
||||
// 生成粒子,按行按列生成全部粒子
|
||||
private ParticleModel[][] generateParticles(Bitmap bitmap, Rect bound) {
|
||||
int w = bound.width();
|
||||
int h = bound.height();
|
||||
|
||||
// 横向粒子的个数
|
||||
int horizontalCount = w / ParticleModel.PART_WH;
|
||||
// 竖向粒子的个数
|
||||
int verticalCount = h / ParticleModel.PART_WH;
|
||||
|
||||
// 粒子宽度
|
||||
int bitmapPartWidth = horizontalCount == 0 ? 0 : bitmap.getWidth() / horizontalCount;
|
||||
// 粒子高度
|
||||
int bitmapPartHeight = verticalCount == 0 ? 0 : bitmap.getHeight() / verticalCount;
|
||||
|
||||
ParticleModel[][] particles = new ParticleModel[verticalCount][horizontalCount];
|
||||
for (int row = 0; row < verticalCount; row++) {
|
||||
for (int column = 0; column < horizontalCount; column++) {
|
||||
//取得当前粒子所在位置的颜色
|
||||
int color = bitmap.getPixel(column * bitmapPartWidth, row * bitmapPartHeight);
|
||||
|
||||
Point point = new Point(column, row);
|
||||
particles[row][column] = new ParticleModel(color, bound, point);
|
||||
}
|
||||
}
|
||||
return particles;
|
||||
}
|
||||
|
||||
// 由view调用,在View上绘制全部的粒子
|
||||
void draw(Canvas canvas) {
|
||||
// 动画结束时停止
|
||||
if (!isStarted()) {
|
||||
return;
|
||||
}
|
||||
// 遍历粒子,并绘制在View上
|
||||
for (ParticleModel[] particle : mParticles) {
|
||||
for (ParticleModel p : particle) {
|
||||
p.advance((Float) getAnimatedValue());
|
||||
mPaint.setColor(p.color);
|
||||
// 错误的设置方式只是这样设置,透明色会显示为黑色
|
||||
// mPaint.setAlpha((int) (255 * p.alpha));
|
||||
// 正确的设置方式,这样透明颜色就不是黑色了
|
||||
mPaint.setAlpha((int) (Color.alpha(p.color) * p.alpha));
|
||||
canvas.drawCircle(p.cx, p.cy, p.radius, mPaint);
|
||||
}
|
||||
}
|
||||
mContainer.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
mContainer.invalidate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.xscm.moduleutil.widget.animator;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
import com.blankj.utilcode.util.BarUtils;
|
||||
|
||||
public class ExplosionField extends View {
|
||||
private static final String TAG = "ExplosionField";
|
||||
private static final Canvas mCanvas = new Canvas();
|
||||
private ExplosionAnimator animator;
|
||||
|
||||
public ExplosionField(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
animator.draw(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行爆破破碎动画
|
||||
*/
|
||||
public void explode(final View view, final AnimatorListenerAdapter listener) {
|
||||
Rect rect = new Rect();
|
||||
view.getGlobalVisibleRect(rect); //得到view相对于整个屏幕的坐标
|
||||
rect.offset(0, -BarUtils.getStatusBarHeight()); //去掉状态栏高度
|
||||
if (rect.width() == 0 || rect.height() == 0) {
|
||||
return;
|
||||
}
|
||||
animator = new ExplosionAnimator(this, createBitmapFromView(view), rect);
|
||||
|
||||
// 接口回调
|
||||
animator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
if (listener != null) listener.onAnimationStart(animation);
|
||||
// 延时添加到界面上
|
||||
attach2Activity((Activity) getContext());
|
||||
// 让被爆炸的View消失(爆炸的View是新创建的View,原View本身不会发生任何变化)
|
||||
view.animate().alpha(0f).setDuration(150).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (listener != null) listener.onAnimationEnd(animation);
|
||||
// 从界面中移除
|
||||
removeFromActivity((Activity) getContext());
|
||||
// 让被爆炸的View显示(爆炸的View是新创建的View,原View本身不会发生任何变化)
|
||||
view.animate().alpha(1f).setDuration(150).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
if (listener != null) listener.onAnimationCancel(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
if (listener != null) listener.onAnimationRepeat(animation);
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private Bitmap createBitmapFromView(View view) {
|
||||
// 为什么屏蔽以下代码段?
|
||||
// 如果ImageView直接得到位图,那么当它设置背景(backgroud)时,不会读取到背景颜色
|
||||
// if (view instanceof ImageView) {
|
||||
// Drawable drawable = ((ImageView)view).getDrawable();
|
||||
// if (drawable != null && drawable instanceof BitmapDrawable) {
|
||||
// return ((BitmapDrawable) drawable).getBitmap();
|
||||
// }
|
||||
// }
|
||||
//view.clearFocus(); //不同焦点状态显示的可能不同——(azz:不同就不同有什么关系?)
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
|
||||
if (bitmap != null) {
|
||||
synchronized (mCanvas) {
|
||||
mCanvas.setBitmap(bitmap);
|
||||
view.draw(mCanvas);
|
||||
// 清除引用
|
||||
mCanvas.setBitmap(null);
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将创建的ExplosionField添加到Activity上
|
||||
*/
|
||||
private void attach2Activity(Activity activity) {
|
||||
ViewGroup rootView = activity.findViewById(Window.ID_ANDROID_CONTENT);
|
||||
|
||||
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
rootView.addView(this, lp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将ExplosionField从Activity上移除
|
||||
*/
|
||||
private void removeFromActivity(Activity activity) {
|
||||
ViewGroup rootView = activity.findViewById(Window.ID_ANDROID_CONTENT);
|
||||
rootView.removeView(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.xscm.moduleutil.widget.animator;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.blankj.utilcode.util.ConvertUtils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 说明:爆破粒子,每个移动与渐变的小块
|
||||
* 作者:Jian
|
||||
* 时间:2017/12/26.
|
||||
*/
|
||||
class ParticleModel {
|
||||
// 默认小球宽高
|
||||
static final int PART_WH = ConvertUtils.dp2px(5);
|
||||
// 随机数,随机出位置和大小
|
||||
static Random random = new Random();
|
||||
//center x of circle
|
||||
float cx;
|
||||
//center y of circle
|
||||
float cy;
|
||||
// 半径
|
||||
float radius;
|
||||
// 颜色
|
||||
int color;
|
||||
// 透明度
|
||||
float alpha;
|
||||
// 整体边界
|
||||
Rect mBound;
|
||||
|
||||
ParticleModel(int color, Rect bound, Point point) {
|
||||
int row = point.y; //行是高
|
||||
int column = point.x; //列是宽
|
||||
|
||||
this.mBound = bound;
|
||||
this.color = color;
|
||||
this.alpha = 1f;
|
||||
this.radius = PART_WH;
|
||||
this.cx = bound.left + PART_WH * column;
|
||||
this.cy = bound.top + PART_WH * row;
|
||||
}
|
||||
|
||||
// 每一步动画都得重新计算出自己的状态值
|
||||
void advance(float factor) {
|
||||
cx = cx + factor * random.nextInt(mBound.width()) * (random.nextFloat() - 0.5f);
|
||||
cy = cy + factor * random.nextInt(mBound.height() / 2);
|
||||
|
||||
radius = radius - factor * random.nextInt(2);
|
||||
|
||||
alpha = (1f - factor) * (1 + random.nextFloat());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.xscm.moduleutil.widget.animator;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
/**
|
||||
* ProjectName: FarmDemo-master
|
||||
* Package: com.lantern.core.myapplication
|
||||
* Description: 弹簧插值器
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2020/11/10 17:50
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2020/11/10 17:50
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class SpringInterpolator implements Interpolator {
|
||||
//控制弹簧系数
|
||||
private float factor;
|
||||
|
||||
public SpringInterpolator(float factor) {
|
||||
this.factor = factor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getInterpolation(float input) {
|
||||
//factor = 0.4
|
||||
// pow(2, -10 * x) * sin((x - factor / 4) * (2 * PI) / factor) + 1
|
||||
|
||||
return (float) -(Math.pow(2, -10 * input) * Math.sin((input - factor / 4) * (2 * Math.PI) / factor) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.xscm.moduleutil.widget.animator;
|
||||
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
|
||||
/**
|
||||
* ProjectName: FarmDemo-master
|
||||
* Package: com.lantern.core.myapplication
|
||||
* Description: java类作用描述
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2020/11/10 17:51
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2020/11/10 17:51
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class TreeAnimation {
|
||||
public static Animation getAnimation() {
|
||||
// 创建缩放的动画对象
|
||||
ScaleAnimation sa = new ScaleAnimation(1f, 1.0f, 1.0f, 1.1f, ScaleAnimation.RELATIVE_TO_SELF, 0.0f, ScaleAnimation.RELATIVE_TO_SELF, 1.0f);
|
||||
// 设置动画播放的时间
|
||||
sa.setDuration(1500);
|
||||
sa.setInterpolator(new SpringInterpolator(0.3f));
|
||||
return sa;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.xscm.moduleutil.widget.animator;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.blankj.utilcode.util.ConvertUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.utils.GiftAnimatorUtil;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class TreeRewardAnim extends ConstraintLayout {
|
||||
|
||||
public TreeRewardAnim(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public TreeRewardAnim(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public TreeRewardAnim(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void doAnim(String imageUrl) {
|
||||
View view = inflate(getContext(), R.layout.room_view_water_tree_gift_anim, null);
|
||||
view.setScaleX(0.5f);
|
||||
view.setScaleY(0.5f);
|
||||
ImageView imageView = view.findViewById(R.id.image);
|
||||
LayoutParams params = new LayoutParams(ConvertUtils.dp2px(77), ConvertUtils.dp2px(75));
|
||||
params.startToStart = LayoutParams.PARENT_ID;
|
||||
params.endToEnd = LayoutParams.PARENT_ID;
|
||||
params.topToTop = LayoutParams.PARENT_ID;
|
||||
params.bottomToBottom = LayoutParams.PARENT_ID;
|
||||
view.setLayoutParams(params);
|
||||
addView(view);
|
||||
ImageUtils.loadImageView(imageUrl, imageView);
|
||||
ObjectAnimator tada = GiftAnimatorUtil.tada(view);
|
||||
PropertyValuesHolder objectAnimatorY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, view.getY(), view.getY() - new Random().nextInt(ConvertUtils.dp2px(100)));
|
||||
int max = this.getRight(), min = this.getLeft();
|
||||
int ran2 = new Random().nextInt(max - min + 1) + min;
|
||||
int random = new Random().nextInt(4);
|
||||
int x = new Random().nextInt(ConvertUtils.dp2px(120));
|
||||
if (random > 1) {
|
||||
x = -x;
|
||||
}
|
||||
PropertyValuesHolder objectAnimatorX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, view.getX(), x);
|
||||
ObjectAnimator valueAnimator = ObjectAnimator.ofPropertyValuesHolder(view, objectAnimatorY, objectAnimatorX).
|
||||
setDuration(1000);
|
||||
PropertyValuesHolder scaleAnimatorY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.5f, 1);
|
||||
PropertyValuesHolder scaleAnimatorX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0.5f, 1);
|
||||
ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(view, scaleAnimatorY, scaleAnimatorX).
|
||||
setDuration(1000);
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
set.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
removeView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
removeView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animator) {
|
||||
|
||||
}
|
||||
});
|
||||
set.playSequentially(valueAnimator, scaleAnimator, tada);
|
||||
set.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.xscm.moduleutil.widget.animator;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.blankj.utilcode.util.ScreenUtils;
|
||||
import com.xscm.moduleutil.utils.GiftAnimatorUtil;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
|
||||
public class WheatGiftAnim {
|
||||
|
||||
public static void addGift(View targetView, String imageUrl) {
|
||||
final int[] location = new int[2];
|
||||
targetView.getLocationOnScreen(location);
|
||||
ImageView imageView = new ImageView(targetView.getContext());
|
||||
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(targetView.getWidth(), targetView.getHeight());
|
||||
imageView.setLayoutParams(params);
|
||||
ImageUtils.loadImageView(imageUrl, imageView);
|
||||
attach2Activity((Activity) targetView.getContext(), imageView);
|
||||
ObjectAnimator tada = GiftAnimatorUtil.tada(imageView);
|
||||
PropertyValuesHolder objectAnimatorY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, ScreenUtils.getScreenHeight(), location[1]);
|
||||
PropertyValuesHolder objectAnimatorX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, ScreenUtils.getScreenWidth() / 2f, location[0]);
|
||||
ObjectAnimator valueAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, objectAnimatorY, objectAnimatorX).
|
||||
setDuration(1200);
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
set.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
removeFromActivity(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
removeFromActivity(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animator) {
|
||||
|
||||
}
|
||||
});
|
||||
set.playSequentially(valueAnimator, tada);
|
||||
set.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将创建的ExplosionField添加到Activity上
|
||||
*/
|
||||
private static void attach2Activity(Activity activity, View view) {
|
||||
ViewGroup rootView = activity.findViewById(Window.ID_ANDROID_CONTENT);
|
||||
rootView.addView(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将ExplosionField从Activity上移除
|
||||
*
|
||||
* @param imageView
|
||||
*/
|
||||
private static void removeFromActivity(ImageView imageView) {
|
||||
ViewGroup rootView = ((Activity) imageView.getContext()).findViewById(Window.ID_ANDROID_CONTENT);
|
||||
rootView.removeView(imageView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
|
||||
public abstract class BaseDialog<VDM extends ViewDataBinding> extends Dialog {
|
||||
|
||||
protected VDM mBinding;
|
||||
|
||||
public BaseDialog(@NonNull Context context) {
|
||||
this(context, R.style.BaseDialogStyle);
|
||||
}
|
||||
|
||||
public BaseDialog(@NonNull Context context, int themeResId) {
|
||||
super(context, themeResId);
|
||||
mBinding = DataBindingUtil.bind(LayoutInflater.from(context).inflate(getLayoutId(), null, false));
|
||||
if (mBinding != null) {
|
||||
setContentView(mBinding.getRoot());
|
||||
}
|
||||
getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
|
||||
initView();
|
||||
initData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
super.dismiss();
|
||||
if (mBinding!=null){
|
||||
mBinding.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract int getLayoutId();
|
||||
|
||||
public abstract void initView();
|
||||
|
||||
public abstract void initData();
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.adapter.LikeListAdapter;
|
||||
import com.xscm.moduleutil.base.BaseMvpDialogFragment;
|
||||
import com.xscm.moduleutil.bean.CircleListBean;
|
||||
import com.xscm.moduleutil.databinding.FragmentCommentDialogBinding;
|
||||
import com.xscm.moduleutil.presenter.CommentContacts;
|
||||
import com.xscm.moduleutil.presenter.CommentPresenter;
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout;
|
||||
import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*@author qx
|
||||
*@data 2025/5/30
|
||||
*@description: 点赞列表弹窗
|
||||
*/
|
||||
public class CommentDialogFragment extends BaseMvpDialogFragment<CommentPresenter, FragmentCommentDialogBinding> implements
|
||||
CommentContacts.View {
|
||||
private int page;
|
||||
private LikeListAdapter likeListAdapter;
|
||||
@Override
|
||||
protected CommentPresenter bindPresenter() {
|
||||
return new CommentPresenter(this, getActivity());
|
||||
}
|
||||
public static void show(String id, FragmentManager fragmentManager) {
|
||||
CommentDialogFragment dialogFragment = new CommentDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("id", id); // 可选:传递参数
|
||||
dialogFragment.setArguments(args);
|
||||
dialogFragment.show(fragmentManager, "CommentDialogFragment");
|
||||
}
|
||||
@Override
|
||||
protected void initData() {
|
||||
MvpPre.getCommentList(getArguments().getString("id"));
|
||||
}
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
Window window = getDialog().getWindow();
|
||||
if (window != null) {
|
||||
// 设置固定高度为 500dp
|
||||
int heightInDp = 450;
|
||||
int heightInPx = (int) (heightInDp * getResources().getDisplayMetrics().density);
|
||||
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, heightInPx);
|
||||
|
||||
// 可选:设置动画样式(从底部弹出)
|
||||
window.setWindowAnimations(R.style.CommonShowDialogBottom);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
mBinding.srl.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
|
||||
@Override
|
||||
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
|
||||
page++;
|
||||
MvpPre.getCommentList(getArguments().getString("id"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh(@NonNull RefreshLayout refreshLayout) {
|
||||
// EventBus.getDefault().post(new BannerRefreshEvent());
|
||||
page = 1;
|
||||
MvpPre.getCommentList(getArguments().getString("id"));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mBinding.rvComment.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
likeListAdapter = new LikeListAdapter();
|
||||
mBinding.rvComment.setAdapter(likeListAdapter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void initDialogStyle(Window window) {
|
||||
super.initDialogStyle(window);
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
}
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_comment_dialog;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void getCommentList(List<CircleListBean.LikeList> likeLists) {
|
||||
mBinding.tvNum.setText("已有"+likeLists.size()+"人点赞");
|
||||
likeListAdapter.setNewData(likeLists);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.blankj.utilcode.util.ScreenUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
public class CommonDialog extends BaseDialog {
|
||||
|
||||
TextView tvContent;
|
||||
TextView textViewLeft;
|
||||
TextView textViewRight;
|
||||
|
||||
private OnClickListener mOnClickListener;
|
||||
|
||||
|
||||
public CommonDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayoutId() {
|
||||
return R.layout.dialog_common;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initView() {
|
||||
getWindow().setLayout((int) (ScreenUtils.getScreenWidth() * 0.86), ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
tvContent = mBinding.getRoot().findViewById(R.id.tv_content);
|
||||
textViewLeft = mBinding.getRoot().findViewById(R.id.tv_left);
|
||||
textViewRight = mBinding.getRoot().findViewById(R.id.tv_right);
|
||||
textViewLeft.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
mOnClickListener.onLeftClick();
|
||||
}
|
||||
});
|
||||
textViewRight.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
mOnClickListener.onRightClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initData() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void setContent(String content) {
|
||||
tvContent.setText(content);
|
||||
}
|
||||
|
||||
public void setLeftText(String leftText) {
|
||||
textViewLeft.setText(leftText);
|
||||
}
|
||||
|
||||
public void setRightText(String rightText) {
|
||||
textViewRight.setText(rightText);
|
||||
}
|
||||
|
||||
public void setmOnClickListener(OnClickListener onClickListener) {
|
||||
this.mOnClickListener = onClickListener;
|
||||
}
|
||||
|
||||
|
||||
public interface OnClickListener {
|
||||
void onLeftClick();
|
||||
|
||||
void onRightClick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 项目名称 qipao-android
|
||||
* 包名:com.qpyy.room.widget
|
||||
* 创建人 黄强
|
||||
* 创建时间 2020/8/6 16:55
|
||||
* 描述 自定义数字键盘
|
||||
*/
|
||||
|
||||
public class KeyboardPopupWindow extends PopupWindow {
|
||||
private static final String TAG = "KeyboardPopupWindow";
|
||||
private Context context;
|
||||
private View anchorView;
|
||||
private View parentView;
|
||||
private EditText editText;
|
||||
private boolean isRandomSort = false;//数字是否随机排序
|
||||
private List<Integer> list = new ArrayList<>();
|
||||
private int[] commonButtonIds = new int[]{R.id.bt_keyboard1, R.id.bt_keyboard2, R.id.bt_keyboard3, R.id.bt_keyboard4,
|
||||
R.id.bt_keyboard5, R.id.bt_keyboard6, R.id.bt_keyboard7, R.id.bt_keyboard8, R.id.bt_keyboard9, R.id.bt_keyboard0};
|
||||
|
||||
private KeyboardCompleteListener mListener;
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @param anchorView
|
||||
* @param
|
||||
* @param
|
||||
*/
|
||||
public KeyboardPopupWindow(Context context, View anchorView) {
|
||||
this.context = context;
|
||||
this.anchorView = anchorView;
|
||||
this.isRandomSort = isRandomSort;
|
||||
if (context == null || anchorView == null) {
|
||||
return;
|
||||
}
|
||||
initConfig();
|
||||
initView();
|
||||
Log.d(TAG, "KeyboardPopupWindow: =====================打开了自定义键盘");
|
||||
}
|
||||
|
||||
|
||||
private void initConfig() {
|
||||
setOutsideTouchable(false);
|
||||
setFocusable(false);
|
||||
setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
forbidDefaultSoftKeyboard();
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止系统默认的软键盘弹出
|
||||
*/
|
||||
private void forbidDefaultSoftKeyboard() {
|
||||
if (editText == null) {
|
||||
return;
|
||||
}
|
||||
if (android.os.Build.VERSION.SDK_INT > 10) {//4.0以上,使用反射的方式禁止系统自带的软键盘弹出
|
||||
try {
|
||||
Class<EditText> cls = EditText.class;
|
||||
Method setShowSoftInputOnFocus;
|
||||
setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
|
||||
setShowSoftInputOnFocus.setAccessible(true);
|
||||
setShowSoftInputOnFocus.invoke(editText, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新自定义的popupwindow是否outside可触摸反应:如果是不可触摸的,则显示该软键盘view
|
||||
*
|
||||
* @param isTouchable
|
||||
*/
|
||||
public void refreshKeyboardOutSideTouchable(boolean isTouchable) {
|
||||
setOutsideTouchable(isTouchable);
|
||||
if (!isTouchable) {
|
||||
show();
|
||||
} else {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
parentView = LayoutInflater.from(context).inflate(R.layout.room_custom_num_keyboard, null);
|
||||
initKeyboardView(parentView);
|
||||
setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
setContentView(parentView);
|
||||
this.setBackgroundDrawable(new BitmapDrawable());
|
||||
this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
this.setFocusable(true);
|
||||
this.setOutsideTouchable(true);
|
||||
this.setTouchable(true);
|
||||
this.setAnimationStyle(R.style.mypopwindow_anim_style);
|
||||
}
|
||||
|
||||
private void initKeyboardView(View view) {
|
||||
//初始化编辑框
|
||||
editText = view.findViewById(R.id.et_keyboard_input);
|
||||
editText.setFocusable(false);//禁止编辑框获取焦点
|
||||
//①给数字键设置点击监听
|
||||
for (int i = 0; i < commonButtonIds.length; i++) {
|
||||
final Button button = view.findViewById(commonButtonIds[i]);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int curSelection = editText.getSelectionStart();
|
||||
int length = editText.getText().toString().length();
|
||||
if (curSelection < length) {
|
||||
String content = editText.getText().toString();
|
||||
editText.setText(content.substring(0, curSelection) + button.getText() + content.subSequence(curSelection, length));
|
||||
editText.setSelection(curSelection + 1);
|
||||
} else {
|
||||
if (length == 0 && button.getText().toString().equals("0") || equals("")) {
|
||||
return;
|
||||
}
|
||||
editText.setText(editText.getText().toString() + button.getText());
|
||||
editText.setSelection(editText.getText().toString().length());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//③给叉按键设置点击监听
|
||||
view.findViewById(R.id.rl_keyboard_cross).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int length = editText.getText().toString().length();
|
||||
int curSelection = editText.getSelectionStart();
|
||||
if (length > 0 && curSelection > 0 && curSelection <= length) {
|
||||
String content = editText.getText().toString();
|
||||
editText.setText(content.substring(0, curSelection - 1) + content.subSequence(curSelection, length));
|
||||
editText.setSelection(curSelection - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//③给叉按键设置点击监听
|
||||
view.findViewById(R.id.iv_keyboard_cross).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int length = editText.getText().toString().length();
|
||||
int curSelection = editText.getSelectionStart();
|
||||
if (length > 0 && curSelection > 0 && curSelection <= length) {
|
||||
String content = editText.getText().toString();
|
||||
editText.setText(content.substring(0, curSelection - 1) + content.subSequence(curSelection, length));
|
||||
editText.setSelection(curSelection - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//确定
|
||||
view.findViewById(R.id.bt_keyboard_ok).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.d(TAG, "onClick: 您输入的数量是:" + editText.getText().toString());
|
||||
mListener.inputComplete(editText.getText().toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void show() {
|
||||
if (!isShowing() && anchorView != null) {
|
||||
// doRandomSortOp();
|
||||
this.showAtLocation(anchorView, Gravity.BOTTOM, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机分布数字
|
||||
*/
|
||||
private void doRandomSortOp() {
|
||||
if (parentView == null) {
|
||||
return;
|
||||
}
|
||||
if (!isRandomSort) {
|
||||
for (int i = 0; i < commonButtonIds.length; i++) {
|
||||
final Button button = parentView.findViewById(commonButtonIds[i]);
|
||||
button.setText("" + i);
|
||||
}
|
||||
} else {
|
||||
list.clear();
|
||||
Random ran = new Random();
|
||||
while (list.size() < commonButtonIds.length) {
|
||||
int n = ran.nextInt(commonButtonIds.length);
|
||||
if (!list.contains(n))
|
||||
list.add(n);
|
||||
}
|
||||
for (int i = 0; i < commonButtonIds.length; i++) {
|
||||
final Button button = parentView.findViewById(commonButtonIds[i]);
|
||||
button.setText("" + list.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseResources() {
|
||||
this.dismiss();
|
||||
context = null;
|
||||
anchorView = null;
|
||||
if (list != null) {
|
||||
list.clear();
|
||||
list = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addRoomPasswordListener(KeyboardCompleteListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public interface KeyboardCompleteListener {
|
||||
|
||||
void inputComplete(String inputContent);//输入完成
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.blankj.utilcode.util.ToastUtils;
|
||||
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||
import com.chad.library.adapter.base.BaseViewHolder;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.base.BaseMvpDialogFragment;
|
||||
import com.xscm.moduleutil.bean.GiftLabelBean;
|
||||
import com.xscm.moduleutil.bean.GiftPackBean;
|
||||
import com.xscm.moduleutil.bean.RewardUserBean;
|
||||
import com.xscm.moduleutil.bean.RoonGiftModel;
|
||||
import com.xscm.moduleutil.bean.WalletBean;
|
||||
import com.xscm.moduleutil.bean.room.RoomAuction;
|
||||
import com.xscm.moduleutil.databinding.DialogRewardFragmentBinding;
|
||||
import com.xscm.moduleutil.databinding.FragmentRewardGiftDialogBinding;
|
||||
import com.xscm.moduleutil.presenter.RewardGiftContacts;
|
||||
import com.xscm.moduleutil.presenter.RewardGiftPresenter;
|
||||
import com.xscm.moduleutil.utils.ChatLauncher;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout;
|
||||
import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author qx
|
||||
* @data 2025/6/16
|
||||
* @description: 广场中所有的打赏榜单列表
|
||||
*/
|
||||
public class RewardDialogFragment extends BaseMvpDialogFragment<RewardGiftPresenter, DialogRewardFragmentBinding> implements RewardGiftContacts.View {
|
||||
private String circle_id;
|
||||
private BaseQuickAdapter<RewardUserBean, BaseViewHolder> adapter;
|
||||
private int page;
|
||||
|
||||
public static void show(String id, FragmentManager fragmentManager) {
|
||||
RewardDialogFragment dialogFragment = new RewardDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("circle_id", id); // 可选:传递参数
|
||||
dialogFragment.setArguments(args);
|
||||
dialogFragment.show(fragmentManager, "RewardDialogFragment");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
circle_id = getArguments().getString("circle_id");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDialogStyle(Window window) {
|
||||
super.initDialogStyle(window);
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
window = getDialog().getWindow();
|
||||
if (window != null) {
|
||||
// 设置固定高度为 500dp
|
||||
int screenHeight = getResources().getDisplayMetrics().heightPixels;
|
||||
int heightInDp = (int) (screenHeight * 0.8f);;
|
||||
int heightInPx = (int) heightInDp;
|
||||
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, heightInPx);
|
||||
|
||||
// 可选:设置动画样式(从底部弹出)
|
||||
window.setWindowAnimations(com.xscm.moduleutil.R.style.CommonShowDialogBottom);
|
||||
// 强制支持 adjustResize 行为
|
||||
// window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RewardGiftPresenter bindPresenter() {
|
||||
return new RewardGiftPresenter(this, getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
MvpPre.getRewardList(circle_id, 1, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
mBinding.srl.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
|
||||
@Override
|
||||
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
|
||||
page++;
|
||||
MvpPre.getRewardList(circle_id, page, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh(@NonNull RefreshLayout refreshLayout) {
|
||||
// EventBus.getDefault().post(new BannerRefreshEvent());
|
||||
page = 1;
|
||||
MvpPre.getRewardList(circle_id, page, 10);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mBinding.rvComment.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
adapter = new BaseQuickAdapter<RewardUserBean, BaseViewHolder>(R.layout.item_dialog_reward, null) {
|
||||
@Override
|
||||
protected void convert(BaseViewHolder helper, RewardUserBean item) {
|
||||
int position = helper.getLayoutPosition();
|
||||
|
||||
TextView tv_rank = helper.getView(R.id.rank_icon);
|
||||
|
||||
if (position < 3) {
|
||||
|
||||
switch (position) {
|
||||
case 0:
|
||||
|
||||
tv_rank.setBackground(getResources().getDrawable(R.mipmap.rewar_1));
|
||||
break;
|
||||
case 1:
|
||||
tv_rank.setBackground(getResources().getDrawable(R.mipmap.rewar_2));
|
||||
break;
|
||||
case 2:
|
||||
tv_rank.setBackground(getResources().getDrawable(R.mipmap.rewar_3));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tv_rank.setText(String.valueOf(position + 1)); // 显示从1开始的序号
|
||||
}
|
||||
|
||||
// 绑定用户信息
|
||||
helper.setText(R.id.tvName, item.getNickname());
|
||||
helper.setText(R.id.tvPic, item.getTotal_price());
|
||||
ImageUtils.loadHeadCC(item.getAvatar(), helper.getView(R.id.ivAvatar));
|
||||
helper.getView(R.id.tvScore).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ChatLauncher.getInstance().launchC2CChat(getActivity(), item.getUser_id());
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
mBinding.rvComment.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.dialog_reward_fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRewardList(List<RewardUserBean> rewardUserBeanList) {
|
||||
adapter.addData(rewardUserBeanList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getGiftLabel(List<GiftLabelBean> giftLabelBeans) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGiftList(List<RoonGiftModel> roonGiftModels, int type) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void giveGift() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wallet(WalletBean walletBean) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reward_zone() {
|
||||
ToastUtils.showShort("打赏成功");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void roomAuctionJoin(RoomAuction.AuctionListBean auctionListBean) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void giftPack(List<GiftPackBean> giftPackBean) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||
import com.chad.library.adapter.base.BaseViewHolder;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.adapter.GiftTwoDetailsFragment;
|
||||
import com.xscm.moduleutil.base.BaseMvpDialogFragment;
|
||||
import com.xscm.moduleutil.bean.GiftLabelBean;
|
||||
import com.xscm.moduleutil.bean.GiftNumBean;
|
||||
import com.xscm.moduleutil.bean.GiftPackBean;
|
||||
import com.xscm.moduleutil.bean.RewardUserBean;
|
||||
import com.xscm.moduleutil.bean.RoonGiftModel;
|
||||
import com.xscm.moduleutil.bean.WalletBean;
|
||||
import com.xscm.moduleutil.bean.room.RoomAuction;
|
||||
import com.xscm.moduleutil.color.ThemeableDrawableUtils;
|
||||
import com.xscm.moduleutil.databinding.FragmentRewardGiftDialogBinding;
|
||||
import com.xscm.moduleutil.dialog.RechargeDialogFragment;
|
||||
import com.xscm.moduleutil.event.GiftRewardEvent;
|
||||
import com.xscm.moduleutil.presenter.RewardGiftContacts;
|
||||
import com.xscm.moduleutil.presenter.RewardGiftPresenter;
|
||||
import com.xscm.moduleutil.utils.ColorManager;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.xscm.moduleutil.widget.GifAvatarOvalView;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author qx
|
||||
* @data 2025/5/29
|
||||
* @description: 打赏礼物的fragment
|
||||
*/
|
||||
public class RewardGiftDialogFragment extends BaseMvpDialogFragment<RewardGiftPresenter, FragmentRewardGiftDialogBinding> implements RewardGiftContacts.View {
|
||||
|
||||
private BaseQuickAdapter<RewardUserBean, BaseViewHolder> adapter;
|
||||
private SelectGiftNumPopupWindow mSelectGiftNumPopupWindow;
|
||||
private KeyboardPopupWindow mKeyboardPopupWindow;
|
||||
private List<GiftNumBean> mGiftNumList;
|
||||
private String circle_id;
|
||||
private RoonGiftModel roonGiftModel = null;
|
||||
private List<Fragment> fragmentList = new ArrayList<>();
|
||||
private String user_id;
|
||||
private String giftNumber = "";
|
||||
private int point;
|
||||
@Override
|
||||
protected RewardGiftPresenter bindPresenter() {
|
||||
return new RewardGiftPresenter(this, getActivity());
|
||||
}
|
||||
public static void show(String id,String userId,int point, FragmentManager fragmentManager) {
|
||||
RewardGiftDialogFragment dialogFragment = new RewardGiftDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("circle_id", id); // 可选:传递参数
|
||||
args.putString("user_id", userId);
|
||||
args.putInt("point",point);
|
||||
// 设置参数...
|
||||
dialogFragment.setArguments(args);
|
||||
dialogFragment.show(fragmentManager, "RewardGiftDialogFragment");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
circle_id=getArguments().getString("circle_id");
|
||||
user_id=getArguments().getString("user_id");
|
||||
point=getArguments().getInt("point");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDialogStyle(Window window) {
|
||||
super.initDialogStyle(window);
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
}
|
||||
@Override
|
||||
protected void initData() {
|
||||
MvpPre.getRewardList(circle_id, 1, 10);
|
||||
MvpPre.getGiftLabel("1");
|
||||
MvpPre.wallet();
|
||||
mGiftNumList=new ArrayList<>();
|
||||
mGiftNumList.add(new GiftNumBean("20", "x20"));
|
||||
mGiftNumList.add(new GiftNumBean("15", "x15"));
|
||||
mGiftNumList.add(new GiftNumBean("10", "x10"));
|
||||
mGiftNumList.add(new GiftNumBean("5", "x5"));
|
||||
mGiftNumList.add(new GiftNumBean("1", "x1"));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
mBinding.recycleView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
adapter = new BaseQuickAdapter<RewardUserBean, BaseViewHolder>(com.xscm.moduleutil.R.layout.item_reward1, null) {
|
||||
@Override
|
||||
protected void convert(BaseViewHolder helper, RewardUserBean item) {
|
||||
GifAvatarOvalView gifAvatarOvalView = helper.getView(com.xscm.moduleutil.R.id.im_reward1);
|
||||
if (item!=null ) {
|
||||
ImageUtils.loadHeadCC(item.getAvatar(), helper.getView(com.xscm.moduleutil.R.id.im_reward1));
|
||||
}
|
||||
}
|
||||
};
|
||||
mBinding.recycleView.setAdapter(adapter);
|
||||
mBinding.tvGiveCoinNum.setOnClickListener(this::onClisk);
|
||||
mBinding.tvRewardNum.setOnClickListener(this::onClisk);
|
||||
mBinding.cz.setOnClickListener(this::onClisk);
|
||||
mBinding.tvGive.setOnClickListener(this::onClisk);
|
||||
|
||||
float[] corners = {0f, 65f, 65f, 0f};
|
||||
ThemeableDrawableUtils.setThemeableRoundedBackground( mBinding.tvGive, ColorManager.getInstance().getPrimaryColorInt(), corners);
|
||||
mBinding.tvGive.setTextColor(ColorManager.getInstance().getButtonColorInt());
|
||||
mBinding.cz.setTextColor(ColorManager.getInstance().getPrimaryColorInt());
|
||||
}
|
||||
|
||||
private void onClisk(View view1) {
|
||||
if (view1.getId()==R.id.tv_give_coin_num){
|
||||
if (mSelectGiftNumPopupWindow == null) {
|
||||
|
||||
mSelectGiftNumPopupWindow = new SelectGiftNumPopupWindow(getSelfActivity(), (adapter, view, position) -> {
|
||||
GiftNumBean giftNumBean = (GiftNumBean) adapter.getItem(position);
|
||||
if ("0".equals(giftNumBean.getNumber())) {//自定义
|
||||
mKeyboardPopupWindow = new KeyboardPopupWindow(getSelfActivity(), getView());
|
||||
mKeyboardPopupWindow.refreshKeyboardOutSideTouchable(false);//false开启键盘 ,true关闭键盘
|
||||
mKeyboardPopupWindow.addRoomPasswordListener(new KeyboardPopupWindow.KeyboardCompleteListener() {//监听自定键盘的完成
|
||||
@Override
|
||||
public void inputComplete(String inputContent) {
|
||||
mBinding.tvGiveCoinNum.setText(inputContent);
|
||||
mKeyboardPopupWindow.releaseResources();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mBinding.tvGiveCoinNum.setText(giftNumBean.getNumber());
|
||||
}
|
||||
mSelectGiftNumPopupWindow.dismiss();
|
||||
});
|
||||
}
|
||||
mSelectGiftNumPopupWindow.setData(mGiftNumList);
|
||||
mSelectGiftNumPopupWindow.showAtLocation(mBinding.tvGiveCoinNum, Gravity.BOTTOM | Gravity.RIGHT, 100, 230);
|
||||
|
||||
}else if (view1.getId()== com.xscm.moduleutil.R.id.tv_reward_num){
|
||||
RewardDialogFragment.show(circle_id,getChildFragmentManager());
|
||||
}else if (view1.getId()== R.id.cz){
|
||||
RechargeDialogFragment.show("",null, getActivity().getSupportFragmentManager());
|
||||
}else if (view1.getId()== R.id.tv_give){
|
||||
for (int i=0;i<mGiftNumList.size();i++) {
|
||||
if (mBinding.tvGiveCoinNum.getText().toString().equals(mGiftNumList.get(i).getText())) {
|
||||
giftNumber = mGiftNumList.get(i).getNumber();
|
||||
}
|
||||
}
|
||||
giveGift(giftNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_reward_gift_dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRewardList(List<RewardUserBean> rewardUserBeanList) {
|
||||
if (rewardUserBeanList != null && !rewardUserBeanList.isEmpty()) {
|
||||
mBinding.recycleView.setVisibility(View.VISIBLE);
|
||||
mBinding.tvRewardNum.setVisibility(View.VISIBLE);
|
||||
mBinding.tvRewardTitle.setVisibility(View.INVISIBLE);
|
||||
mBinding.tvRewardNum.setText("已有" + String.valueOf(rewardUserBeanList.size()) + "个人打赏");
|
||||
int limit = Math.min(rewardUserBeanList.size(), 5);
|
||||
List<RewardUserBean> limitedList = rewardUserBeanList.subList(0, limit);
|
||||
adapter.setNewData(limitedList);
|
||||
} else {
|
||||
adapter.setNewData(null);
|
||||
mBinding.recycleView.setVisibility(View.INVISIBLE);
|
||||
mBinding.tvRewardNum.setVisibility(View.INVISIBLE);
|
||||
mBinding.tvRewardTitle.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getGiftLabel(List<GiftLabelBean> giftLabelBeans) {
|
||||
mBinding.viewPager.setAdapter(new MyFragmentPagerAdapter(getChildFragmentManager(), giftLabelBeans,fragmentList));
|
||||
mBinding.slidingTabLayout.setViewPager(mBinding.viewPager);
|
||||
mBinding.slidingTabLayout.setCurrentTab(0);
|
||||
}
|
||||
private int getSelectedGift() {
|
||||
int currentItem = mBinding.viewPager.getCurrentItem();
|
||||
// if (currentItem < 1) { //比2小是才是礼物
|
||||
GiftTwoDetailsFragment fragment = (GiftTwoDetailsFragment) fragmentList.get(currentItem);
|
||||
roonGiftModel = fragment.getGiftList();
|
||||
// }
|
||||
// else {
|
||||
// GiftFragment fragment = (GiftFragment) fragmentList.get(currentItem);
|
||||
// giftModel = fragment.getmData();
|
||||
// }
|
||||
return currentItem;
|
||||
}
|
||||
|
||||
private void giveGift(String num) {
|
||||
getSelectedGift();
|
||||
int currentItem = mBinding.viewPager.getCurrentItem();
|
||||
String userId = user_id;
|
||||
if (currentItem < 1) {
|
||||
if (roonGiftModel == null) {
|
||||
ToastUtils.show("请选择礼物");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (TextUtils.isEmpty(num)) {
|
||||
ToastUtils.show("请选择打赏礼物数量");
|
||||
return;
|
||||
}
|
||||
if (Integer.valueOf(num) <= 0) {
|
||||
ToastUtils.show("请选择打赏礼物数量");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (roonGiftModel == null) {
|
||||
ToastUtils.show("请选择礼物");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(num)) {
|
||||
ToastUtils.show("请选择打赏礼物数量");
|
||||
return;
|
||||
}
|
||||
if (Integer.valueOf(num) <= 0) {
|
||||
ToastUtils.show("请选择打赏礼物数量");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentItem == 0) {
|
||||
//礼物打赏
|
||||
giftNumber = num;
|
||||
MvpPre.reward_zone(circle_id, roonGiftModel.getGift_id(), num, "1");
|
||||
} else {
|
||||
//背包礼物打赏
|
||||
// giftNumber = num;
|
||||
// roomGiftGiveEvent = new RoomGiftGiveEvent(userId, roomId, pit, num, 1, giftModel, null);
|
||||
//
|
||||
// MvpPre.giveBackGift(userId, giftModel.getGift_id(), roomId, pit, num, giftModel, 1);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void setGiftList(List<RoonGiftModel> roonGiftModels,int type) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void giveGift() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wallet(WalletBean walletBean) {
|
||||
mBinding.tvRewardGift.setText(walletBean.getCoin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reward_zone() {
|
||||
com.blankj.utilcode.util.ToastUtils.showShort("打赏成功");
|
||||
EventBus.getDefault().post(new GiftRewardEvent(point));
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void roomAuctionJoin(RoomAuction.AuctionListBean auctionListBean) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void giftPack(List<GiftPackBean> giftPackBean) {
|
||||
|
||||
}
|
||||
|
||||
private static class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private List<GiftLabelBean> list;
|
||||
private List<Fragment> fragmentList ;
|
||||
|
||||
public MyFragmentPagerAdapter(FragmentManager fm, List<GiftLabelBean> list,List<Fragment> fragmentList) {
|
||||
super(fm);
|
||||
this.list = list;
|
||||
this.fragmentList = fragmentList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
GiftLabelBean model = list.get(position);
|
||||
Fragment fragment = GiftTwoDetailsFragment.newInstance(model.getId(), 2);
|
||||
fragmentList.add(fragment); // 保存 Fragment 实例
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
GiftLabelBean model = list.get(position);
|
||||
return model.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||
import com.chad.library.adapter.base.BaseViewHolder;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.GiftNumBean;
|
||||
|
||||
/**
|
||||
*@author qx
|
||||
*@data 2025/5/30
|
||||
*@description: 展示礼物打赏数量的适配器
|
||||
*/
|
||||
public class SelectGiftNumAdapter extends BaseQuickAdapter<GiftNumBean, BaseViewHolder> {
|
||||
|
||||
public SelectGiftNumAdapter() {
|
||||
super(R.layout.room_rv_item_gift_num_windows);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void convert(BaseViewHolder helper, GiftNumBean item) {
|
||||
// helper.setText(R.id.tv_gift_mum, item.getNumber())
|
||||
helper.setText(R.id.tv_gift_name, item.getText());
|
||||
if ("0".equals(item.getNumber())) {
|
||||
// helper.setText(R.id.tv_custom, item.getText());
|
||||
// helper.setVisible(R.id.tv_custom,true);
|
||||
// helper.setVisible(R.id.tv_gift_mum, false).
|
||||
helper.setVisible(R.id.tv_gift_name, false);
|
||||
}else {
|
||||
// helper.setVisible(R.id.tv_custom,false);
|
||||
// helper.setVisible(R.id.tv_gift_mum, false)
|
||||
helper.setVisible(R.id.tv_gift_name, true);
|
||||
}
|
||||
if("1".equals(item.getNumber())){
|
||||
helper.getView(R.id.iv_split).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.GiftNumBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*@author qx
|
||||
*@data 2025/5/30
|
||||
*@description: 选择礼物数量窗口
|
||||
*/
|
||||
public class SelectGiftNumPopupWindow extends PopupWindow {
|
||||
private View rootView;
|
||||
private SelectGiftNumAdapter selectGiftNumAdapter;
|
||||
|
||||
public SelectGiftNumPopupWindow(Context context, BaseQuickAdapter.OnItemClickListener onItemClickListener) {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
rootView = inflater.inflate(R.layout.room_window_pop_gift, null);
|
||||
RecyclerView recyclerView = rootView.findViewById(R.id.rv_gift_num_window);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(context));
|
||||
recyclerView.setAdapter(selectGiftNumAdapter = new SelectGiftNumAdapter());
|
||||
|
||||
selectGiftNumAdapter.setOnItemClickListener(onItemClickListener);
|
||||
setContentView(rootView);
|
||||
this.setBackgroundDrawable(new BitmapDrawable());
|
||||
this.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
this.setFocusable(true);
|
||||
this.setOutsideTouchable(true);
|
||||
this.setTouchable(true);
|
||||
this.setAnimationStyle(R.style.mypopwindow_anim_style);
|
||||
}
|
||||
|
||||
|
||||
public void setData(List<GiftNumBean> data) {
|
||||
if (selectGiftNumAdapter != null) {
|
||||
selectGiftNumAdapter.setNewData(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.xscm.moduleutil.widget.dialog;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static com.blankj.utilcode.util.ActivityUtils.startActivity;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.blankj.utilcode.util.ImageUtils;
|
||||
import com.blankj.utilcode.util.ToastUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.activity.WebViewActivity;
|
||||
import com.xscm.moduleutil.bean.CircleListBean;
|
||||
import com.xscm.moduleutil.databinding.RoomDialogShareBinding;
|
||||
import com.xscm.moduleutil.utils.BaseBottomSheetDialog;
|
||||
import com.xscm.moduleutil.utils.SpUtil;
|
||||
import com.xscm.moduleutil.utils.wx.WeChatShareUtils;
|
||||
import com.tencent.mm.opensdk.modelmsg.SendMessageToWX;
|
||||
|
||||
|
||||
/**
|
||||
* 房间分享弹窗
|
||||
*/
|
||||
public class ShareDialog extends BaseBottomSheetDialog<RoomDialogShareBinding> {
|
||||
public WeChatShareUtils weChatShareUtils;
|
||||
private Context mContext;
|
||||
private String mcontent;
|
||||
private String murl;
|
||||
private String ids;
|
||||
private int types;//1:用户 2:房间;3:动态
|
||||
private String mUserId;
|
||||
private CircleListBean bean;
|
||||
|
||||
public interface OnShareDataListener {
|
||||
void onShareDataLoaded(String id);
|
||||
}
|
||||
|
||||
private OnShareDataListener listener;
|
||||
|
||||
public void setOnShareDataListener(OnShareDataListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private static final String TAG = "ShareDialog";
|
||||
|
||||
public ShareDialog(Context context, String content, String url, String id, int type,String userId,CircleListBean bean) {
|
||||
super(context);
|
||||
this.mcontent = content;
|
||||
this.murl = url;
|
||||
this.mContext = getContext();
|
||||
this.ids = id;
|
||||
this.types = type;
|
||||
this.mUserId = userId;
|
||||
this.bean = bean;
|
||||
Log.i(TAG, "(Start)启动了===========================ShareDialog");
|
||||
iniv();
|
||||
}
|
||||
|
||||
private void iniv() {
|
||||
if (types == 1) {//1是分享动态
|
||||
|
||||
} else if (types == 2) {//分享相册
|
||||
mBinding.tvQq.setVisibility(GONE);
|
||||
mBinding.tvQqq.setVisibility(GONE);
|
||||
mBinding.rl.setVisibility(GONE);
|
||||
}
|
||||
if (mUserId.equals(SpUtil.getUserId()+"")){
|
||||
mBinding.shanc.setVisibility(VISIBLE);
|
||||
}else {
|
||||
mBinding.shanc.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayout() {
|
||||
return R.layout.room_dialog_share;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initView() {
|
||||
Window window = getWindow();
|
||||
WindowManager.LayoutParams lp = window.getAttributes();
|
||||
lp.dimAmount = 0.4f;
|
||||
window.setAttributes(lp);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
mBinding.tvQq.setOnClickListener(this::onClick);
|
||||
mBinding.tvWx.setOnClickListener(this::onClick);
|
||||
mBinding.tvWxq.setOnClickListener(this::onClick);
|
||||
mBinding.tvQqq.setOnClickListener(this::onClick);
|
||||
mBinding.tvClean.setOnClickListener(this::onClick);
|
||||
mBinding.tvCopy.setOnClickListener(this::onClick);
|
||||
mBinding.tvJub.setOnClickListener(this::onClick);
|
||||
mBinding.shanc.setOnClickListener(this::onClick);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initData() {
|
||||
weChatShareUtils = WeChatShareUtils.getInstance(getContext());
|
||||
// if (mUserId.equals(SpUtil.getUserId()+"")){
|
||||
// mBinding.shanc.setVisibility(GONE);
|
||||
// }else {
|
||||
// mBinding.shanc.setVisibility(VISIBLE);
|
||||
// }
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
int id = view.getId();
|
||||
// String appName = CommonAppContext.getInstance().getResources().getString(R.string.app_name);
|
||||
|
||||
|
||||
if (R.id.tv_qq == id) {
|
||||
// AppLogUtil.reportAppLog(AppLogEvent.D0108, "share_way", "QQ分享");
|
||||
// ShareUtil.shareWeb((Activity) view.getContext(), URLConstants.SHARE, String.format("我在%s玩呢,大家都在玩,快来展示您的精彩", appName)
|
||||
// , appName, "", R.mipmap.ic_launcher_new, SHARE_MEDIA.QQ);
|
||||
} else if (R.id.tv_wx == id) {
|
||||
// AppLogUtil.reportAppLog(AppLogEvent.D0108, "share_way", "微信分享");
|
||||
// ShareUtil.shareWeb((Activity) view.getContext(), URLConstants.SHARE, String.format("我在%s玩呢,大家都在玩,快来展示您的精彩", appName)
|
||||
// , appName, "", R.mipmap.ic_launcher_new, SHARE_MEDIA.WEIXIN);
|
||||
fun_handleShare(SendMessageToWX.Req.WXSceneSession);
|
||||
} else if (R.id.tv_wxq == id) {
|
||||
fun_handleShare1(SendMessageToWX.Req.WXSceneTimeline);
|
||||
} else if (R.id.tv_qqq == id) {
|
||||
|
||||
} else if (R.id.shanc == id) {
|
||||
if (listener != null) {
|
||||
listener.onShareDataLoaded(ids);
|
||||
}
|
||||
} else if (R.id.tv_jub == id) {
|
||||
if (types == 3) {
|
||||
Intent intent = new Intent(getContext(), WebViewActivity.class);
|
||||
intent.putExtra("url", "https://vespa.qxmier.com/web/index.html#/pages/feedback/report?id=" + SpUtil.getToken() + "&fromType=" + types + "&fromId=" + bean.getId());
|
||||
intent.putExtra("title", "举报");
|
||||
startActivity(intent);
|
||||
}else if (types == 1){
|
||||
Intent intent = new Intent(getContext(), WebViewActivity.class);
|
||||
intent.putExtra("url", "https://vespa.qxmier.com/web/index.html#/pages/feedback/report?id=" + SpUtil.getToken() + "&fromType=" + types + "&fromId=" + bean.getUser_id());
|
||||
intent.putExtra("title", "举报");
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
} else if (R.id.tv_copy == id) {
|
||||
ClipboardManager clipboard = (ClipboardManager)mContext.getSystemService( Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("链接",bean.getShare_url() );
|
||||
if (clipboard != null) {
|
||||
clipboard.setPrimaryClip(clip);
|
||||
com.hjq.toast.ToastUtils.show("复制成功");
|
||||
} else {
|
||||
com.hjq.toast.ToastUtils.show("复制失败");
|
||||
}
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
// todo fun_handleShare 分享小程序类型 (只支持分享给微信好友)
|
||||
private void fun_handleShare(int scene) {
|
||||
if (weChatShareUtils.isSupportWX()) {
|
||||
String appName = mContext.getPackageManager().getApplicationLabel(mContext.getApplicationInfo()).toString();
|
||||
if (types == 2){
|
||||
com.xscm.moduleutil.utils.ImageUtils.loadBitmap(murl, new com.xscm.moduleutil.utils.ImageUtils.onLoadBitmap() {
|
||||
@Override
|
||||
public void onReady(Bitmap resource) {
|
||||
weChatShareUtils.sharePic(resource, scene);
|
||||
}
|
||||
});
|
||||
}else {
|
||||
String shortContent = mcontent.length() > 20 ? mcontent.substring(0, 20) : mcontent;
|
||||
String desc = shortContent;
|
||||
String title = appName;
|
||||
String url = murl;
|
||||
Bitmap bitmap = ImageUtils.drawable2Bitmap(mContext.getResources().getDrawable(R.mipmap.ic_launcher));
|
||||
weChatShareUtils.shareUrl(url, title, bitmap, desc, scene);
|
||||
}
|
||||
} else {
|
||||
ToastUtils.showShort("手机上微信版本不支持分享功能");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void fun_handleShare1(int scene) {
|
||||
if (weChatShareUtils.isSupportWX()) {
|
||||
String appName = mContext.getPackageManager().getApplicationLabel(mContext.getApplicationInfo()).toString();
|
||||
if (types == 2) {
|
||||
com.xscm.moduleutil.utils.ImageUtils.loadBitmap(murl, new com.xscm.moduleutil.utils.ImageUtils.onLoadBitmap() {
|
||||
@Override
|
||||
public void onReady(Bitmap resource) {
|
||||
weChatShareUtils.sharePic(resource, scene);
|
||||
}
|
||||
});
|
||||
}else {
|
||||
String shortContent = mcontent.length() > 20 ? mcontent.substring(0, 20) : mcontent;
|
||||
String desc = shortContent;
|
||||
String title = appName;
|
||||
String url = murl;
|
||||
// Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),R.mipmap.ic_launcher);
|
||||
Bitmap bitmap = ImageUtils.drawable2Bitmap(mContext.getResources().getDrawable(R.mipmap.ic_launcher));
|
||||
weChatShareUtils.shareUrl(url, title, bitmap, desc, scene);
|
||||
}
|
||||
} else {
|
||||
ToastUtils.showShort("手机上微信版本不支持分享功能");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package com.xscm.moduleutil.widget.floatingView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.blankj.utilcode.util.BarUtils;
|
||||
|
||||
/**
|
||||
* @Author $
|
||||
* @Time $ $
|
||||
* @Description $
|
||||
*/
|
||||
public class Floa extends FrameLayout {
|
||||
public static final int MARGIN_EDGE = 13;
|
||||
private float mOriginalRawX;
|
||||
private float mOriginalRawY;
|
||||
private float mOriginalX;
|
||||
private float mOriginalY;
|
||||
private MagnetViewListener mMagnetViewListener;
|
||||
private static final int TOUCH_TIME_THRESHOLD = 150;
|
||||
private long mLastTouchDownTime;
|
||||
protected MoveAnimator mMoveAnimator;
|
||||
protected int mScreenWidth;
|
||||
private int mScreenHeight;
|
||||
private int mStatusBarHeight;
|
||||
private boolean isNearestLeft = true;
|
||||
private float mPortraitY;
|
||||
|
||||
public void setMagnetViewListener(MagnetViewListener magnetViewListener) {
|
||||
this.mMagnetViewListener = magnetViewListener;
|
||||
}
|
||||
public interface MagnetViewListener {
|
||||
void onClick(Floa floa);
|
||||
}
|
||||
public Floa(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public Floa(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public Floa(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mMoveAnimator = new MoveAnimator();
|
||||
mStatusBarHeight = BarUtils.getStatusBarHeight();
|
||||
setClickable(true);
|
||||
updateSize();
|
||||
// 确保视图初始位置在屏幕内
|
||||
setX(Math.max(0, Math.min(getX(), mScreenWidth - getWidth())));
|
||||
setY(Math.max(mStatusBarHeight, Math.min(getY(), mScreenHeight - getHeight())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event == null) {
|
||||
return false;
|
||||
}
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
changeOriginalTouchParams(event);
|
||||
updateSize();
|
||||
mMoveAnimator.stop();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
Log.d("FloatView", "ACTION_MOVE: " + event.getX() + ", " + event.getY()); // 添加日志
|
||||
updateViewPosition(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
clearPortraitY();
|
||||
moveToEdge();
|
||||
if (isOnClickEvent()) {
|
||||
dealClickEvent();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void dealClickEvent() {
|
||||
if (mMagnetViewListener != null) {
|
||||
mMagnetViewListener.onClick(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isOnClickEvent() {
|
||||
return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD;
|
||||
}
|
||||
|
||||
private void updateViewPosition(MotionEvent event) {
|
||||
// 计算新的Y位置
|
||||
float desY = mOriginalY + event.getRawY() - mOriginalRawY;
|
||||
// 限制Y位置不超出屏幕高度
|
||||
if (desY < mStatusBarHeight) {
|
||||
desY = mStatusBarHeight;
|
||||
}
|
||||
if (desY > mScreenHeight - getHeight()) {
|
||||
desY = mScreenHeight - getHeight();
|
||||
}
|
||||
|
||||
// 计算新的X位置
|
||||
float desX = mOriginalX + event.getRawX() - mOriginalRawX;
|
||||
// 限制X位置不超出屏幕宽度
|
||||
if (desX < 0) {
|
||||
desX = 0;
|
||||
}
|
||||
if (desX > mScreenWidth - getWidth()) {
|
||||
desX = mScreenWidth - getWidth();
|
||||
}
|
||||
|
||||
// 设置视图的新位置
|
||||
setX(desX);
|
||||
setY(desY);
|
||||
|
||||
// 确保视图可见
|
||||
if (getVisibility() != VISIBLE) {
|
||||
setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void changeOriginalTouchParams(MotionEvent event) {
|
||||
mOriginalX = getX();//getX()相对于控件X坐标的距离
|
||||
mOriginalY = getY();
|
||||
mOriginalRawX = event.getRawX();//getRawX()指控件在屏幕上的X坐标
|
||||
mOriginalRawY = event.getRawY();
|
||||
mLastTouchDownTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
protected void updateSize() {
|
||||
ViewGroup viewGroup = (ViewGroup) getParent();
|
||||
if (viewGroup != null) {
|
||||
mScreenWidth = viewGroup.getWidth() ;
|
||||
mScreenHeight = viewGroup.getHeight();
|
||||
}else {
|
||||
// 如果父视图为空,使用屏幕的实际宽度和高度
|
||||
mScreenWidth = getResources().getDisplayMetrics().widthPixels;
|
||||
mScreenHeight = getResources().getDisplayMetrics().heightPixels;
|
||||
}
|
||||
// mScreenWidth = (SystemUtils.getScreenWidth(getContext()) - this.getWidth());
|
||||
// mScreenHeight = SystemUtils.getScreenHeight(getContext());
|
||||
}
|
||||
|
||||
public void moveToEdge() {
|
||||
moveToEdge(isNearestLeft(), false);
|
||||
}
|
||||
|
||||
public void moveToEdge(boolean isLeft, boolean isLandscape) {
|
||||
float moveDistance = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
|
||||
float y = getY();
|
||||
if (!isLandscape && mPortraitY != 0) {
|
||||
y = mPortraitY;
|
||||
clearPortraitY();
|
||||
}
|
||||
mMoveAnimator.start(moveDistance, Math.min(Math.max(0, y), mScreenHeight - getHeight()));
|
||||
}
|
||||
|
||||
private void clearPortraitY() {
|
||||
mPortraitY = 0;
|
||||
}
|
||||
|
||||
protected boolean isNearestLeft() {
|
||||
int middle = mScreenWidth / 2;
|
||||
isNearestLeft = getX() < middle;
|
||||
return isNearestLeft;
|
||||
}
|
||||
|
||||
public void onRemove() {
|
||||
if (mMagnetViewListener != null) {
|
||||
mMagnetViewListener=null;
|
||||
}
|
||||
}
|
||||
|
||||
protected class MoveAnimator implements Runnable {
|
||||
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
private float destinationX;
|
||||
private float destinationY;
|
||||
private long startingTime;
|
||||
|
||||
void start(float x, float y) {
|
||||
this.destinationX = x;
|
||||
this.destinationY = y;
|
||||
startingTime = System.currentTimeMillis();
|
||||
handler.post(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (getRootView() == null || getRootView().getParent() == null) {
|
||||
return;
|
||||
}
|
||||
float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
|
||||
float deltaX = (destinationX - getX()) * progress;
|
||||
float deltaY = (destinationY - getY()) * progress;
|
||||
move(deltaX, deltaY);
|
||||
if (progress < 1) {
|
||||
handler.post(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
handler.removeCallbacks(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void move(float deltaX, float deltaY) {
|
||||
setX(getX() + deltaX);
|
||||
setY(getY() + deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (getParent() != null) {
|
||||
final boolean isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||
markPortraitY(isLandscape);
|
||||
((ViewGroup) getParent()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateSize();
|
||||
moveToEdge(isNearestLeft, isLandscape);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void markPortraitY(boolean isLandscape) {
|
||||
if (isLandscape) {
|
||||
mPortraitY = getY();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.xscm.moduleutil.widget.floatingView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.blankj.utilcode.util.BarUtils;
|
||||
import com.blankj.utilcode.util.ScreenUtils;
|
||||
|
||||
/**
|
||||
* 可拖拽悬浮
|
||||
* 稳定版悬浮窗控件 - 支持点击/拖动分离、适配横竖屏、自动吸附边缘
|
||||
*/
|
||||
public class FloatingMagnetView extends FrameLayout {
|
||||
|
||||
public static final int MARGIN_EDGE = 0;
|
||||
private float mOriginalX, mOriginalY; // 当前 View 的坐标
|
||||
private float mOriginalRawX, mOriginalRawY; // 触摸点的原始屏幕坐标
|
||||
private long mLastTouchDownTime;
|
||||
private boolean isDragging = false;
|
||||
private boolean isNearestLeft = true;
|
||||
|
||||
private int mScreenWidth;
|
||||
private int mScreenHeight;
|
||||
private int mStatusBarHeight;
|
||||
|
||||
private MoveAnimator mMoveAnimator;
|
||||
|
||||
private OnFloatingClickListener listener;
|
||||
|
||||
private static final int TOUCH_TIME_THRESHOLD = 150;
|
||||
private static final float CLICK_DRAG_THRESHOLD = 10;
|
||||
|
||||
public FloatingMagnetView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FloatingMagnetView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public FloatingMagnetView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mMoveAnimator = new MoveAnimator();
|
||||
mStatusBarHeight = BarUtils.getStatusBarHeight();
|
||||
setClickable(true);
|
||||
updateSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateSize();
|
||||
moveToEdge(isNearestLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
return false; // 不拦截,让子控件优先处理点击
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
float dx = Math.abs(ev.getX() - (mOriginalRawX - mOriginalX));
|
||||
float dy = Math.abs(ev.getY() - (mOriginalRawY - mOriginalY));
|
||||
if (dx > CLICK_DRAG_THRESHOLD || dy > CLICK_DRAG_THRESHOLD) {
|
||||
isDragging = true;
|
||||
}
|
||||
return isDragging;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
isDragging = false;
|
||||
return false;
|
||||
|
||||
default:
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event == null) return false;
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mOriginalX = getX();
|
||||
mOriginalY = getY();
|
||||
mOriginalRawX = event.getRawX();
|
||||
mOriginalRawY = event.getRawY();
|
||||
mLastTouchDownTime = System.currentTimeMillis();
|
||||
mMoveAnimator.stop();
|
||||
isDragging = false;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (isDragging) {
|
||||
float newX = mOriginalX + (event.getRawX() - mOriginalRawX);
|
||||
float newY = mOriginalY + (event.getRawY() - mOriginalRawY);
|
||||
|
||||
// 限制 Y 轴边界
|
||||
if (newY < mStatusBarHeight) newY = mStatusBarHeight;
|
||||
if (newY > mScreenHeight - getHeight()) newY = mScreenHeight - getHeight();
|
||||
|
||||
setX(newX);
|
||||
setY(newY);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (isOnClickEvent()) {
|
||||
if (listener != null) {
|
||||
listener.onClick();
|
||||
}
|
||||
} else {
|
||||
moveToEdge();
|
||||
}
|
||||
isDragging = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isOnClickEvent() {
|
||||
long time = System.currentTimeMillis() - mLastTouchDownTime;
|
||||
float dx = Math.abs(getX() - mOriginalX);
|
||||
float dy = Math.abs(getY() - mOriginalY);
|
||||
return time < TOUCH_TIME_THRESHOLD && dx < CLICK_DRAG_THRESHOLD && dy < CLICK_DRAG_THRESHOLD;
|
||||
}
|
||||
|
||||
private void updateSize() {
|
||||
mScreenWidth = ScreenUtils.getScreenWidth() - getWidth();
|
||||
mScreenHeight = ScreenUtils.getScreenHeight();
|
||||
}
|
||||
|
||||
public void moveToEdge() {
|
||||
moveToEdge(isNearestLeft());
|
||||
}
|
||||
|
||||
public void moveToEdge(boolean isLeft) {
|
||||
float moveX = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
|
||||
mMoveAnimator.start(moveX, getY());
|
||||
isNearestLeft = isLeft;
|
||||
}
|
||||
|
||||
protected boolean isNearestLeft() {
|
||||
int middle = mScreenWidth / 2;
|
||||
return getX() < middle;
|
||||
}
|
||||
|
||||
protected class MoveAnimator implements Runnable {
|
||||
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
private float destinationX, destinationY;
|
||||
private long startTime;
|
||||
|
||||
void start(float x, float y) {
|
||||
this.destinationX = x;
|
||||
this.destinationY = y;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
handler.post(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (getRootView() == null || getRootView().getParent() == null) return;
|
||||
|
||||
float progress = Math.min(1f, (System.currentTimeMillis() - startTime) / 400f);
|
||||
float deltaX = (destinationX - getX()) * progress;
|
||||
float deltaY = (destinationY - getY()) * progress;
|
||||
|
||||
setX(getX() + deltaX);
|
||||
setY(getY() + deltaY);
|
||||
|
||||
if (progress < 1f) {
|
||||
handler.post(this);
|
||||
}
|
||||
}
|
||||
|
||||
void stop() {
|
||||
handler.removeCallbacks(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnFloatingClickListener(OnFloatingClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public interface OnFloatingClickListener {
|
||||
void onClick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.xscm.moduleutil.widget.floatingView;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
import com.xscm.moduleutil.base.CommonAppContext;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
|
||||
/**
|
||||
* @ClassName FloatingView
|
||||
* @Description 悬浮窗管理器
|
||||
*/
|
||||
public class FloatingView implements IFloatingView {
|
||||
private View mFloatingView;
|
||||
private static volatile FloatingView mInstance;
|
||||
private WeakReference<FrameLayout> mContainer;
|
||||
@LayoutRes
|
||||
private int mLayoutId;
|
||||
private ViewGroup.LayoutParams mLayoutParams = getParams();
|
||||
|
||||
private FloatingView() {
|
||||
}
|
||||
|
||||
public static FloatingView get() {
|
||||
if (mInstance == null) {
|
||||
synchronized (FloatingView.class) {
|
||||
if (mInstance == null) {
|
||||
mInstance = new FloatingView();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView remove() {
|
||||
orderId = 0;
|
||||
if (mFloatingView == null) {
|
||||
return this;
|
||||
}
|
||||
if (ViewCompat.isAttachedToWindow(mFloatingView) && getContainer() != null) {
|
||||
getContainer().removeView(mFloatingView);
|
||||
}
|
||||
mFloatingView = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ensureFloatingView() {
|
||||
synchronized (this) {
|
||||
if (mFloatingView != null) {
|
||||
addViewToWindow(mFloatingView);
|
||||
return;
|
||||
}
|
||||
View view = LayoutInflater.from(CommonAppContext.getInstance()).inflate(mLayoutId, null);
|
||||
mFloatingView = view;
|
||||
view.setLayoutParams(mLayoutParams);
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
}
|
||||
});
|
||||
addViewToWindow(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView add() {
|
||||
ensureFloatingView();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView attach(Activity activity) {
|
||||
attach(getActivityRoot(activity));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView attach(FrameLayout container) {
|
||||
if (container == null || mFloatingView == null) {
|
||||
mContainer = new WeakReference<>(container);
|
||||
return this;
|
||||
}
|
||||
if (mFloatingView.getParent() == container) {
|
||||
return this;
|
||||
}
|
||||
if (getContainer() != null && mFloatingView.getParent() == getContainer()) {
|
||||
getContainer().removeView(mFloatingView);
|
||||
}
|
||||
mContainer = new WeakReference<>(container);
|
||||
container.addView(mFloatingView);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView detach(Activity activity) {
|
||||
detach(getActivityRoot(activity));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView detach(FrameLayout container) {
|
||||
if (mFloatingView != null && container != null && ViewCompat.isAttachedToWindow(mFloatingView)) {
|
||||
container.removeView(mFloatingView);
|
||||
}
|
||||
if (getContainer() == container) {
|
||||
mContainer = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return mFloatingView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView customView(ViewGroup viewGroup) {
|
||||
mFloatingView = viewGroup;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView customView(@LayoutRes int resource) {
|
||||
mLayoutId = resource;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingView layoutParams(ViewGroup.LayoutParams params) {
|
||||
mLayoutParams = params;
|
||||
if (mFloatingView != null) {
|
||||
mFloatingView.setLayoutParams(params);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addViewToWindow(final View view) {
|
||||
if (getContainer() == null) {
|
||||
return;
|
||||
}
|
||||
getContainer().addView(view);
|
||||
}
|
||||
|
||||
private FrameLayout getContainer() {
|
||||
if (mContainer == null) {
|
||||
return null;
|
||||
}
|
||||
return mContainer.get();
|
||||
}
|
||||
|
||||
private FrameLayout.LayoutParams getParams() {
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT,
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
params.gravity = Gravity.BOTTOM | Gravity.START;
|
||||
params.setMargins(13, params.topMargin, params.rightMargin, 500);
|
||||
return params;
|
||||
}
|
||||
|
||||
private FrameLayout getActivityRoot(Activity activity) {
|
||||
if (activity == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (FrameLayout) activity.getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private boolean isExpand = true;
|
||||
|
||||
/**
|
||||
* 设置展开或者收起
|
||||
*
|
||||
* @param expand
|
||||
*/
|
||||
public void setExpand(boolean expand) {
|
||||
isExpand = expand;
|
||||
}
|
||||
|
||||
private int orderId;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.xscm.moduleutil.widget.floatingView;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
|
||||
|
||||
public interface IFloatingView {
|
||||
|
||||
FloatingView remove();
|
||||
|
||||
FloatingView add();
|
||||
|
||||
FloatingView attach(Activity activity);
|
||||
|
||||
FloatingView attach(FrameLayout container);
|
||||
|
||||
FloatingView detach(Activity activity);
|
||||
|
||||
FloatingView detach(FrameLayout container);
|
||||
|
||||
View getView();
|
||||
|
||||
FloatingView customView(ViewGroup viewGroup);
|
||||
|
||||
FloatingView customView(@LayoutRes int resource);
|
||||
|
||||
FloatingView layoutParams(ViewGroup.LayoutParams params);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.xscm.moduleutil.widget.floatingView;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
import com.xscm.moduleutil.base.CommonAppContext;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
|
||||
/**
|
||||
* @ClassName FloatingView
|
||||
* @Description 悬浮窗管理器
|
||||
*/
|
||||
public class NotifyFloatingView {
|
||||
private View mFloatingView;
|
||||
private static volatile NotifyFloatingView mInstance;
|
||||
private WeakReference<FrameLayout> mContainer;
|
||||
@LayoutRes
|
||||
private int mLayoutId;
|
||||
private ViewGroup.LayoutParams mLayoutParams = getParams();
|
||||
|
||||
private NotifyFloatingView() {
|
||||
|
||||
}
|
||||
|
||||
public static NotifyFloatingView get() {
|
||||
if (mInstance == null) {
|
||||
synchronized (NotifyFloatingView.class) {
|
||||
if (mInstance == null) {
|
||||
mInstance = new NotifyFloatingView();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
|
||||
public NotifyFloatingView remove() {
|
||||
if (mFloatingView == null) {
|
||||
return this;
|
||||
}
|
||||
if (ViewCompat.isAttachedToWindow(mFloatingView) && getContainer() != null) {
|
||||
getContainer().removeView(mFloatingView);
|
||||
}
|
||||
mFloatingView = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private void ensureFloatingView() {
|
||||
synchronized (this) {
|
||||
if (mFloatingView != null) {
|
||||
addViewToWindow(mFloatingView);
|
||||
return;
|
||||
}
|
||||
View view = LayoutInflater.from(CommonAppContext.getInstance()).inflate(mLayoutId, null);
|
||||
mFloatingView = view;
|
||||
view.setLayoutParams(mLayoutParams);
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
}
|
||||
});
|
||||
addViewToWindow(view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public NotifyFloatingView add() {
|
||||
ensureFloatingView();
|
||||
handler.removeCallbacks(action);
|
||||
handler.postDelayed(action, 3000);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public NotifyFloatingView attach(Activity activity) {
|
||||
attach(getActivityRoot(activity));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public NotifyFloatingView attach(FrameLayout container) {
|
||||
if (container == null || mFloatingView == null) {
|
||||
mContainer = new WeakReference<>(container);
|
||||
return this;
|
||||
}
|
||||
if (mFloatingView.getParent() == container) {
|
||||
return this;
|
||||
}
|
||||
if (getContainer() != null && mFloatingView.getParent() == getContainer()) {
|
||||
getContainer().removeView(mFloatingView);
|
||||
}
|
||||
mContainer = new WeakReference<>(container);
|
||||
container.addView(mFloatingView);
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotifyFloatingView detach(Activity activity) {
|
||||
detach(getActivityRoot(activity));
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotifyFloatingView detach(FrameLayout container) {
|
||||
if (mFloatingView != null && container != null && ViewCompat.isAttachedToWindow(mFloatingView)) {
|
||||
container.removeView(mFloatingView);
|
||||
}
|
||||
if (getContainer() == container) {
|
||||
mContainer = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public View getView() {
|
||||
return mFloatingView;
|
||||
}
|
||||
|
||||
|
||||
public NotifyFloatingView customView(ViewGroup viewGroup) {
|
||||
mFloatingView = viewGroup;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public NotifyFloatingView customView(@LayoutRes int resource) {
|
||||
mLayoutId = resource;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public NotifyFloatingView layoutParams(ViewGroup.LayoutParams params) {
|
||||
mLayoutParams = params;
|
||||
if (mFloatingView != null) {
|
||||
mFloatingView.setLayoutParams(params);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addViewToWindow(final View view) {
|
||||
if (getContainer() == null) {
|
||||
return;
|
||||
}
|
||||
getContainer().addView(view);
|
||||
}
|
||||
|
||||
private FrameLayout getContainer() {
|
||||
if (mContainer == null) {
|
||||
return null;
|
||||
}
|
||||
return mContainer.get();
|
||||
}
|
||||
|
||||
private FrameLayout.LayoutParams getParams() {
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT,
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
params.gravity = Gravity.BOTTOM | Gravity.START;
|
||||
params.setMargins(13, params.topMargin, params.rightMargin, 500);
|
||||
return params;
|
||||
}
|
||||
|
||||
private FrameLayout getActivityRoot(Activity activity) {
|
||||
if (activity == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (FrameLayout) activity.getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private Runnable action = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NotifyFloatingView.get().remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.xscm.moduleutil.widget.img;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 看大图
|
||||
*/
|
||||
public class FullScreenUtil {
|
||||
|
||||
public static void showFullScreenDialog(Context context, final int pos, final List<String> imgList) {
|
||||
final Dialog dialog = new Dialog(context, R.style.big_pic_dialog);
|
||||
//设置是否允许Dialog可以被点击取消,也会阻止Back键
|
||||
dialog.setCancelable(true);
|
||||
dialog.setCanceledOnTouchOutside(true);
|
||||
Window window = dialog.getWindow();
|
||||
window.setGravity(Gravity.CENTER);
|
||||
//获取Dialog窗体的根容器
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
ViewGroup root = (ViewGroup) dialog.getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
//设置窗口大小为屏幕大小item_img_pv
|
||||
WindowManager wm = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
|
||||
Point screenSize = new Point();
|
||||
wm.getDefaultDisplay().getSize(screenSize);
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(screenSize.x, screenSize.y));
|
||||
// 获取自定义布局,并设置给Dialog
|
||||
View view = inflater.inflate(R.layout.pop_photo_vp, root, false);
|
||||
final ViewPager img_vp = view.findViewById(R.id.img_vp);
|
||||
final TextView img_num_iv = view.findViewById(R.id.img_num_iv);
|
||||
final ImageView img_down_iv = view.findViewById(R.id.img_down_iv);
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
ImgVPAdapter vpAdapter = new ImgVPAdapter(context, imgList);
|
||||
img_vp.setAdapter(vpAdapter);
|
||||
img_vp.setCurrentItem(pos);
|
||||
img_num_iv.setText((pos + 1) + "/" + imgList.size());
|
||||
img_down_iv.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
//保存
|
||||
/* if (imgList.get(pos) != null) {
|
||||
ImageSaveUtils.saveImg(XQDetailActivity.this, imgList.get(pos));
|
||||
}*/
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
img_vp.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
ToastUtils.show("保存成功");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
img_vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(final int position) {
|
||||
img_num_iv.setText((position + 1) + "/" + imgList.size());
|
||||
img_down_iv.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// if (imgList.get(position) != null) {
|
||||
// ImageSaveUtils.saveImg(XQDetailActivity.this, imgList.get(position));
|
||||
// }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
});
|
||||
vpAdapter.setAllClickListener(new ImgVPAdapter.AllClickListener() {
|
||||
@Override
|
||||
public void allclick(int pos) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
dialog.setContentView(view);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.xscm.moduleutil.widget.img;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.widget.ImageSaveUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class ImgVPAdapter extends PagerAdapter {
|
||||
private Context context;
|
||||
private List<String> paths;
|
||||
|
||||
public ImgVPAdapter(Context context, List<String> paths) {
|
||||
this.context = context;
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return paths.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return view == object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, final int position) {
|
||||
ImageView iv_img = (ImageView) LayoutInflater.from(context).inflate(R.layout.item_img_pv, null);
|
||||
// iv_img.setScaleType(ImageView.ScaleType.CENTER);
|
||||
Glide.with(context).load( paths.get(position)).error(R.mipmap.default_image).into(iv_img);
|
||||
iv_img.setScaleType(ImageView.ScaleType.CENTER);
|
||||
iv_img.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (allClickListener != null) {
|
||||
allClickListener.allclick(position);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
iv_img.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(paths.get(position))
|
||||
.into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
||||
ImageSaveUtils.saveImg(context, resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
Toast.makeText(context, "图片加载失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
container.addView(iv_img);
|
||||
return iv_img;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
container.removeView((View) object);
|
||||
}
|
||||
|
||||
public interface AllClickListener {
|
||||
void allclick(int pos);
|
||||
}
|
||||
|
||||
public AllClickListener allClickListener;
|
||||
|
||||
public void setAllClickListener(AllClickListener allClickListener) {
|
||||
this.allClickListener = allClickListener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.xscm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: Pager配置
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:00
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:00
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerConfig {
|
||||
private static final String TAG = "PagerGrid";
|
||||
private static boolean sShowLog = false;
|
||||
private static int sFlingThreshold = 1000; // Fling 阀值,滚动速度超过该阀值才会触发滚动
|
||||
private static float sMillisecondsPreInch = 60f; // 每一个英寸滚动需要的微秒数,数值越大,速度越慢
|
||||
|
||||
/**
|
||||
* 判断是否输出日志
|
||||
*
|
||||
* @return true 输出,false 不输出
|
||||
*/
|
||||
public static boolean isShowLog() {
|
||||
return sShowLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否输出日志
|
||||
*
|
||||
* @param showLog 是否输出
|
||||
*/
|
||||
public static void setShowLog(boolean showLog) {
|
||||
sShowLog = showLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前滚动速度阀值
|
||||
*
|
||||
* @return 当前滚动速度阀值
|
||||
*/
|
||||
public static int getFlingThreshold() {
|
||||
return sFlingThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前滚动速度阀值
|
||||
*
|
||||
* @param flingThreshold 滚动速度阀值
|
||||
*/
|
||||
public static void setFlingThreshold(int flingThreshold) {
|
||||
sFlingThreshold = flingThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取滚动速度 英寸/微秒
|
||||
*
|
||||
* @return 英寸滚动速度
|
||||
*/
|
||||
public static float getMillisecondsPreInch() {
|
||||
return sMillisecondsPreInch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置像素滚动速度 英寸/微秒
|
||||
*
|
||||
* @param millisecondsPreInch 英寸滚动速度
|
||||
*/
|
||||
public static void setMillisecondsPreInch(float millisecondsPreInch) {
|
||||
sMillisecondsPreInch = millisecondsPreInch;
|
||||
}
|
||||
|
||||
//--- 日志 -------------------------------------------------------------------------------------
|
||||
|
||||
public static void Logi(String msg) {
|
||||
if (!PagerConfig.isShowLog()) return;
|
||||
Log.i(TAG, msg);
|
||||
}
|
||||
|
||||
public static void Loge(String msg) {
|
||||
if (!PagerConfig.isShowLog()) return;
|
||||
Log.e(TAG, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,892 @@
|
||||
package com.xscm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||
|
||||
import static com.xscm.moduleutil.widget.pagerecyclerview.PagerConfig.Loge;
|
||||
import static com.xscm.moduleutil.widget.pagerecyclerview.PagerConfig.Logi;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: 分页的网格布局管理器
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:03
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:03
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerGridLayoutManager extends RecyclerView.LayoutManager
|
||||
implements RecyclerView.SmoothScroller.ScrollVectorProvider {
|
||||
|
||||
private static final String TAG = PagerGridLayoutManager.class.getSimpleName();
|
||||
|
||||
public static final int VERTICAL = 0; // 垂直滚动
|
||||
public static final int HORIZONTAL = 1; // 水平滚动
|
||||
|
||||
@IntDef({VERTICAL, HORIZONTAL})
|
||||
public @interface OrientationType {
|
||||
} // 滚动类型
|
||||
|
||||
@OrientationType
|
||||
private int mOrientation; // 默认水平滚动
|
||||
|
||||
private int mOffsetX = 0; // 水平滚动距离(偏移量)
|
||||
private int mOffsetY = 0; // 垂直滚动距离(偏移量)
|
||||
|
||||
private int mRows; // 行数
|
||||
private int mColumns; // 列数
|
||||
private int mOnePageSize; // 一页的条目数量
|
||||
|
||||
private SparseArray<Rect> mItemFrames; // 条目的显示区域
|
||||
|
||||
private int mItemWidth = 0; // 条目宽度
|
||||
private int mItemHeight = 0; // 条目高度
|
||||
|
||||
private int mWidthUsed = 0; // 已经使用空间,用于测量View
|
||||
private int mHeightUsed = 0; // 已经使用空间,用于测量View
|
||||
|
||||
private int mMaxScrollX; // 最大允许滑动的宽度
|
||||
private int mMaxScrollY; // 最大允许滑动的高度
|
||||
private int mScrollState = SCROLL_STATE_IDLE; // 滚动状态
|
||||
|
||||
private boolean mAllowContinuousScroll = true; // 是否允许连续滚动
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param rows 行数
|
||||
* @param columns 列数
|
||||
* @param orientation 方向
|
||||
*/
|
||||
public PagerGridLayoutManager(@IntRange(from = 1, to = 100) int rows,
|
||||
@IntRange(from = 1, to = 100) int columns,
|
||||
@OrientationType int orientation) {
|
||||
mItemFrames = new SparseArray<>();
|
||||
mOrientation = orientation;
|
||||
mRows = rows;
|
||||
mColumns = columns;
|
||||
mOnePageSize = mRows * mColumns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow(RecyclerView view) {
|
||||
super.onAttachedToWindow(view);
|
||||
mRecyclerView = view;
|
||||
}
|
||||
|
||||
//--- 处理布局 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 布局子View
|
||||
*
|
||||
* @param recycler Recycler
|
||||
* @param state State
|
||||
*/
|
||||
@Override
|
||||
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
Logi("Item onLayoutChildren");
|
||||
Logi("Item onLayoutChildren isPreLayout = " + state.isPreLayout());
|
||||
Logi("Item onLayoutChildren isMeasuring = " + state.isMeasuring());
|
||||
Loge("Item onLayoutChildren state = " + state);
|
||||
|
||||
// 如果是 preLayout 则不重新布局
|
||||
if (state.isPreLayout() || !state.didStructureChange()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getItemCount() == 0) {
|
||||
removeAndRecycleAllViews(recycler);
|
||||
// 页面变化回调
|
||||
setPageCount(0);
|
||||
setPageIndex(0, false);
|
||||
return;
|
||||
} else {
|
||||
setPageCount(getTotalPageCount());
|
||||
setPageIndex(getPageIndexByOffset(), false);
|
||||
}
|
||||
|
||||
// 计算页面数量
|
||||
int mPageCount = getItemCount() / mOnePageSize;
|
||||
if (getItemCount() % mOnePageSize != 0) {
|
||||
mPageCount++;
|
||||
}
|
||||
|
||||
// 计算可以滚动的最大数值,并对滚动距离进行修正
|
||||
if (canScrollHorizontally()) {
|
||||
mMaxScrollX = (mPageCount - 1) * getUsableWidth();
|
||||
mMaxScrollY = 0;
|
||||
if (mOffsetX > mMaxScrollX) {
|
||||
mOffsetX = mMaxScrollX;
|
||||
}
|
||||
} else {
|
||||
mMaxScrollX = 0;
|
||||
mMaxScrollY = (mPageCount - 1) * getUsableHeight();
|
||||
if (mOffsetY > mMaxScrollY) {
|
||||
mOffsetY = mMaxScrollY;
|
||||
}
|
||||
}
|
||||
|
||||
// 接口回调
|
||||
// setPageCount(mPageCount);
|
||||
// setPageIndex(mCurrentPageIndex, false);
|
||||
|
||||
Logi("count = " + getItemCount());
|
||||
|
||||
if (mItemWidth <= 0) {
|
||||
mItemWidth = getUsableWidth() / mColumns;
|
||||
}
|
||||
if (mItemHeight <= 0) {
|
||||
mItemHeight = getUsableHeight() / mRows;
|
||||
}
|
||||
|
||||
mWidthUsed = getUsableWidth() - mItemWidth;
|
||||
mHeightUsed = getUsableHeight() - mItemHeight;
|
||||
|
||||
// 预存储两页的View显示区域
|
||||
for (int i = 0; i < mOnePageSize * 2; i++) {
|
||||
getItemFrameByPosition(i);
|
||||
}
|
||||
|
||||
if (mOffsetX == 0 && mOffsetY == 0) {
|
||||
// 预存储View
|
||||
for (int i = 0; i < mOnePageSize; i++) {
|
||||
if (i >= getItemCount()) break; // 防止数据过少时导致数组越界异常
|
||||
View view = recycler.getViewForPosition(i);
|
||||
addView(view);
|
||||
measureChildWithMargins(view, mWidthUsed, mHeightUsed);
|
||||
}
|
||||
}
|
||||
|
||||
// 回收和填充布局
|
||||
recycleAndFillItems(recycler, state, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局结束
|
||||
*
|
||||
* @param state State
|
||||
*/
|
||||
@Override
|
||||
public void onLayoutCompleted(RecyclerView.State state) {
|
||||
super.onLayoutCompleted(state);
|
||||
if (state.isPreLayout()) return;
|
||||
// 页面状态回调
|
||||
setPageCount(getTotalPageCount());
|
||||
setPageIndex(getPageIndexByOffset(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收和填充布局
|
||||
*
|
||||
* @param recycler Recycler
|
||||
* @param state State
|
||||
* @param isStart 是否从头开始,用于控制View遍历方向,true 为从头到尾,false 为从尾到头
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state,
|
||||
boolean isStart) {
|
||||
if (state.isPreLayout()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logi("mOffsetX = " + mOffsetX);
|
||||
Logi("mOffsetY = " + mOffsetY);
|
||||
|
||||
// 计算显示区域区前后多存储一列或则一行
|
||||
Rect displayRect = new Rect(mOffsetX - mItemWidth, mOffsetY - mItemHeight,
|
||||
getUsableWidth() + mOffsetX + mItemWidth, getUsableHeight() + mOffsetY + mItemHeight);
|
||||
// 对显显示区域进行修正(计算当前显示区域和最大显示区域对交集)
|
||||
displayRect.intersect(0, 0, mMaxScrollX + getUsableWidth(), mMaxScrollY + getUsableHeight());
|
||||
Loge("displayRect = " + displayRect.toString());
|
||||
|
||||
int startPos = 0; // 获取第一个条目的Pos
|
||||
int pageIndex = getPageIndexByOffset();
|
||||
startPos = pageIndex * mOnePageSize;
|
||||
Logi("startPos = " + startPos);
|
||||
startPos = startPos - mOnePageSize * 2;
|
||||
if (startPos < 0) {
|
||||
startPos = 0;
|
||||
}
|
||||
int stopPos = startPos + mOnePageSize * 4;
|
||||
if (stopPos > getItemCount()) {
|
||||
stopPos = getItemCount();
|
||||
}
|
||||
|
||||
Loge("startPos = " + startPos);
|
||||
Loge("stopPos = " + stopPos);
|
||||
|
||||
detachAndScrapAttachedViews(recycler); // 移除所有View
|
||||
|
||||
if (isStart) {
|
||||
for (int i = startPos; i < stopPos; i++) {
|
||||
addOrRemove(recycler, displayRect, i);
|
||||
}
|
||||
} else {
|
||||
for (int i = stopPos - 1; i >= startPos; i--) {
|
||||
addOrRemove(recycler, displayRect, i);
|
||||
}
|
||||
}
|
||||
Loge("child count = " + getChildCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加或者移除条目
|
||||
*
|
||||
* @param recycler RecyclerView
|
||||
* @param displayRect 显示区域
|
||||
* @param i 条目下标
|
||||
*/
|
||||
private void addOrRemove(RecyclerView.Recycler recycler, Rect displayRect, int i) {
|
||||
View child = recycler.getViewForPosition(i);
|
||||
Rect rect = getItemFrameByPosition(i);
|
||||
if (!Rect.intersects(displayRect, rect)) {
|
||||
removeAndRecycleView(child, recycler); // 回收入暂存区
|
||||
} else {
|
||||
addView(child);
|
||||
measureChildWithMargins(child, mWidthUsed, mHeightUsed);
|
||||
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
layoutDecorated(child,
|
||||
rect.left - mOffsetX + lp.leftMargin + getPaddingLeft(),
|
||||
rect.top - mOffsetY + lp.topMargin + getPaddingTop(),
|
||||
rect.right - mOffsetX - lp.rightMargin + getPaddingLeft(),
|
||||
rect.bottom - mOffsetY - lp.bottomMargin + getPaddingTop());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- 处理滚动 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 水平滚动
|
||||
*
|
||||
* @param dx 滚动距离
|
||||
* @param recycler 回收器
|
||||
* @param state 滚动状态
|
||||
* @return 实际滚动距离
|
||||
*/
|
||||
@Override
|
||||
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State
|
||||
state) {
|
||||
int newX = mOffsetX + dx;
|
||||
int result = dx;
|
||||
if (newX > mMaxScrollX) {
|
||||
result = mMaxScrollX - mOffsetX;
|
||||
} else if (newX < 0) {
|
||||
result = 0 - mOffsetX;
|
||||
}
|
||||
mOffsetX += result;
|
||||
setPageIndex(getPageIndexByOffset(), true);
|
||||
offsetChildrenHorizontal(-result);
|
||||
if (result > 0) {
|
||||
recycleAndFillItems(recycler, state, true);
|
||||
} else {
|
||||
recycleAndFillItems(recycler, state, false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 垂直滚动
|
||||
*
|
||||
* @param dy 滚动距离
|
||||
* @param recycler 回收器
|
||||
* @param state 滚动状态
|
||||
* @return 实际滚动距离
|
||||
*/
|
||||
@Override
|
||||
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State
|
||||
state) {
|
||||
int newY = mOffsetY + dy;
|
||||
int result = dy;
|
||||
if (newY > mMaxScrollY) {
|
||||
result = mMaxScrollY - mOffsetY;
|
||||
} else if (newY < 0) {
|
||||
result = 0 - mOffsetY;
|
||||
}
|
||||
mOffsetY += result;
|
||||
setPageIndex(getPageIndexByOffset(), true);
|
||||
offsetChildrenVertical(-result);
|
||||
if (result > 0) {
|
||||
recycleAndFillItems(recycler, state, true);
|
||||
} else {
|
||||
recycleAndFillItems(recycler, state, false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听滚动状态,滚动结束后通知当前选中的页面
|
||||
*
|
||||
* @param state 滚动状态
|
||||
*/
|
||||
@Override
|
||||
public void onScrollStateChanged(int state) {
|
||||
Logi("onScrollStateChanged = " + state);
|
||||
mScrollState = state;
|
||||
super.onScrollStateChanged(state);
|
||||
if (state == SCROLL_STATE_IDLE) {
|
||||
setPageIndex(getPageIndexByOffset(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- 私有方法 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取条目显示区域
|
||||
*
|
||||
* @param pos 位置下标
|
||||
* @return 显示区域
|
||||
*/
|
||||
private Rect getItemFrameByPosition(int pos) {
|
||||
Rect rect = mItemFrames.get(pos);
|
||||
if (null == rect) {
|
||||
rect = new Rect();
|
||||
// 计算显示区域 Rect
|
||||
|
||||
// 1. 获取当前View所在页数
|
||||
int page = pos / mOnePageSize;
|
||||
|
||||
// 2. 计算当前页数左上角的总偏移量
|
||||
int offsetX = 0;
|
||||
int offsetY = 0;
|
||||
if (canScrollHorizontally()) {
|
||||
offsetX += getUsableWidth() * page;
|
||||
} else {
|
||||
offsetY += getUsableHeight() * page;
|
||||
}
|
||||
|
||||
// 3. 根据在当前页面中的位置确定具体偏移量
|
||||
int pagePos = pos % mOnePageSize; // 在当前页面中是第几个
|
||||
int row = pagePos / mColumns; // 获取所在行
|
||||
int col = pagePos - (row * mColumns); // 获取所在列
|
||||
|
||||
offsetX += col * mItemWidth;
|
||||
offsetY += row * mItemHeight;
|
||||
|
||||
// 状态输出,用于调试
|
||||
Logi("pagePos = " + pagePos);
|
||||
Logi("行 = " + row);
|
||||
Logi("列 = " + col);
|
||||
|
||||
Logi("offsetX = " + offsetX);
|
||||
Logi("offsetY = " + offsetY);
|
||||
|
||||
rect.left = offsetX;
|
||||
rect.top = offsetY;
|
||||
rect.right = offsetX + mItemWidth;
|
||||
rect.bottom = offsetY + mItemHeight;
|
||||
|
||||
// 存储
|
||||
mItemFrames.put(pos, rect);
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的宽度
|
||||
*
|
||||
* @return 宽度 - padding
|
||||
*/
|
||||
private int getUsableWidth() {
|
||||
return getWidth() - getPaddingLeft() - getPaddingRight();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的高度
|
||||
*
|
||||
* @return 高度 - padding
|
||||
*/
|
||||
private int getUsableHeight() {
|
||||
return getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
}
|
||||
|
||||
|
||||
//--- 页面相关(私有) -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取总页数
|
||||
*/
|
||||
private int getTotalPageCount() {
|
||||
if (getItemCount() <= 0) return 0;
|
||||
int totalCount = getItemCount() / mOnePageSize;
|
||||
if (getItemCount() % mOnePageSize != 0) {
|
||||
totalCount++;
|
||||
}
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据pos,获取该View所在的页面
|
||||
*
|
||||
* @param pos position
|
||||
* @return 页面的页码
|
||||
*/
|
||||
private int getPageIndexByPos(int pos) {
|
||||
return pos / mOnePageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 offset 获取页面Index
|
||||
*
|
||||
* @return 页面 Index
|
||||
*/
|
||||
private int getPageIndexByOffset() {
|
||||
int pageIndex;
|
||||
if (canScrollVertically()) {
|
||||
int pageHeight = getUsableHeight();
|
||||
if (mOffsetY <= 0 || pageHeight <= 0) {
|
||||
pageIndex = 0;
|
||||
} else {
|
||||
pageIndex = mOffsetY / pageHeight;
|
||||
if (mOffsetY % pageHeight > pageHeight / 2) {
|
||||
pageIndex++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int pageWidth = getUsableWidth();
|
||||
if (mOffsetX <= 0 || pageWidth <= 0) {
|
||||
pageIndex = 0;
|
||||
} else {
|
||||
pageIndex = mOffsetX / pageWidth;
|
||||
if (mOffsetX % pageWidth > pageWidth / 2) {
|
||||
pageIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Logi("getPageIndexByOffset pageIndex = " + pageIndex);
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
|
||||
//--- 公开方法 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 创建默认布局参数
|
||||
*
|
||||
* @return 默认布局参数
|
||||
*/
|
||||
@Override
|
||||
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
|
||||
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理测量逻辑
|
||||
*
|
||||
* @param recycler RecyclerView
|
||||
* @param state 状态
|
||||
* @param widthMeasureSpec 宽度属性
|
||||
* @param heightMeasureSpec 高估属性
|
||||
*/
|
||||
@Override
|
||||
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(recycler, state, widthMeasureSpec, heightMeasureSpec);
|
||||
int widthsize = View.MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值
|
||||
int widthmode = View.MeasureSpec.getMode(widthMeasureSpec); //取出宽度的测量模式
|
||||
|
||||
int heightsize = View.MeasureSpec.getSize(heightMeasureSpec); //取出高度的确切数值
|
||||
int heightmode = View.MeasureSpec.getMode(heightMeasureSpec); //取出高度的测量模式
|
||||
|
||||
// 将 wrap_content 转换为 match_parent
|
||||
if (widthmode != EXACTLY && widthsize > 0) {
|
||||
widthmode = EXACTLY;
|
||||
}
|
||||
if (heightmode != EXACTLY && heightsize > 0) {
|
||||
heightmode = EXACTLY;
|
||||
}
|
||||
setMeasuredDimension(View.MeasureSpec.makeMeasureSpec(widthsize, widthmode),
|
||||
View.MeasureSpec.makeMeasureSpec(heightsize, heightmode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以水平滚动
|
||||
*
|
||||
* @return true 是,false 不是。
|
||||
*/
|
||||
@Override
|
||||
public boolean canScrollHorizontally() {
|
||||
return mOrientation == HORIZONTAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以垂直滚动
|
||||
*
|
||||
* @return true 是,false 不是。
|
||||
*/
|
||||
@Override
|
||||
public boolean canScrollVertically() {
|
||||
return mOrientation == VERTICAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到下一页第一个条目的位置
|
||||
*
|
||||
* @return 第一个搞条目的位置
|
||||
*/
|
||||
int findNextPageFirstPos() {
|
||||
int page = mLastPageIndex;
|
||||
page++;
|
||||
if (page >= getTotalPageCount()) {
|
||||
page = getTotalPageCount() - 1;
|
||||
}
|
||||
Loge("computeScrollVectorForPosition next = " + page);
|
||||
return page * mOnePageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到上一页的第一个条目的位置
|
||||
*
|
||||
* @return 第一个条目的位置
|
||||
*/
|
||||
int findPrePageFirstPos() {
|
||||
// 在获取时由于前一页的View预加载出来了,所以获取到的直接就是前一页
|
||||
int page = mLastPageIndex;
|
||||
page--;
|
||||
Loge("computeScrollVectorForPosition pre = " + page);
|
||||
if (page < 0) {
|
||||
page = 0;
|
||||
}
|
||||
Loge("computeScrollVectorForPosition pre = " + page);
|
||||
return page * mOnePageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 X 轴偏移量
|
||||
*
|
||||
* @return X 轴偏移量
|
||||
*/
|
||||
public int getOffsetX() {
|
||||
return mOffsetX;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 Y 轴偏移量
|
||||
*
|
||||
* @return Y 轴偏移量
|
||||
*/
|
||||
public int getOffsetY() {
|
||||
return mOffsetY;
|
||||
}
|
||||
|
||||
|
||||
//--- 页面对齐 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 计算到目标位置需要滚动的距离{@link RecyclerView.SmoothScroller.ScrollVectorProvider}
|
||||
*
|
||||
* @param targetPosition 目标控件
|
||||
* @return 需要滚动的距离
|
||||
*/
|
||||
@Override
|
||||
public PointF computeScrollVectorForPosition(int targetPosition) {
|
||||
PointF vector = new PointF();
|
||||
int[] pos = getSnapOffset(targetPosition);
|
||||
vector.x = pos[0];
|
||||
vector.y = pos[1];
|
||||
return vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取偏移量(为PagerGridSnapHelper准备)
|
||||
* 用于分页滚动,确定需要滚动的距离。
|
||||
* {@link PagerGridSnapHelper}
|
||||
*
|
||||
* @param targetPosition 条目下标
|
||||
*/
|
||||
int[] getSnapOffset(int targetPosition) {
|
||||
int[] offset = new int[2];
|
||||
int[] pos = getPageLeftTopByPosition(targetPosition);
|
||||
offset[0] = pos[0] - mOffsetX;
|
||||
offset[1] = pos[1] - mOffsetY;
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条目下标获取该条目所在页面的左上角位置
|
||||
*
|
||||
* @param pos 条目下标
|
||||
* @return 左上角位置
|
||||
*/
|
||||
private int[] getPageLeftTopByPosition(int pos) {
|
||||
int[] leftTop = new int[2];
|
||||
int page = getPageIndexByPos(pos);
|
||||
if (canScrollHorizontally()) {
|
||||
leftTop[0] = page * getUsableWidth();
|
||||
leftTop[1] = 0;
|
||||
} else {
|
||||
leftTop[0] = 0;
|
||||
leftTop[1] = page * getUsableHeight();
|
||||
}
|
||||
return leftTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需要对齐的View
|
||||
*
|
||||
* @return 需要对齐的View
|
||||
*/
|
||||
public View findSnapView() {
|
||||
if (null != getFocusedChild()) {
|
||||
return getFocusedChild();
|
||||
}
|
||||
if (getChildCount() <= 0) {
|
||||
return null;
|
||||
}
|
||||
int targetPos = getPageIndexByOffset() * mOnePageSize; // 目标Pos
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
int childPos = getPosition(getChildAt(i));
|
||||
if (childPos == targetPos) {
|
||||
return getChildAt(i);
|
||||
}
|
||||
}
|
||||
return getChildAt(0);
|
||||
}
|
||||
|
||||
|
||||
//--- 处理页码变化 -------------------------------------------------------------------------------
|
||||
|
||||
private boolean mChangeSelectInScrolling = true; // 是否在滚动过程中对页面变化回调
|
||||
private int mLastPageCount = -1; // 上次页面总数
|
||||
private int mLastPageIndex = -1; // 上次页面下标
|
||||
|
||||
/**
|
||||
* 设置页面总数
|
||||
*
|
||||
* @param pageCount 页面总数
|
||||
*/
|
||||
private void setPageCount(int pageCount) {
|
||||
if (pageCount >= 0) {
|
||||
if (mPageListener != null && pageCount != mLastPageCount) {
|
||||
mPageListener.onPageSizeChanged(pageCount);
|
||||
}
|
||||
mLastPageCount = pageCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选中页面
|
||||
*
|
||||
* @param pageIndex 页面下标
|
||||
* @param isScrolling 是否处于滚动状态
|
||||
*/
|
||||
private void setPageIndex(int pageIndex, boolean isScrolling) {
|
||||
Loge("setPageIndex = " + pageIndex + ":" + isScrolling);
|
||||
if (pageIndex == mLastPageIndex) return;
|
||||
// 如果允许连续滚动,那么在滚动过程中就会更新页码记录
|
||||
if (isAllowContinuousScroll()) {
|
||||
mLastPageIndex = pageIndex;
|
||||
} else {
|
||||
// 否则,只有等滚动停下时才会更新页码记录
|
||||
if (!isScrolling) {
|
||||
mLastPageIndex = pageIndex;
|
||||
}
|
||||
}
|
||||
if (isScrolling && !mChangeSelectInScrolling) return;
|
||||
if (pageIndex >= 0) {
|
||||
if (null != mPageListener) {
|
||||
mPageListener.onPageSelect(pageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否在滚动状态更新选中页码
|
||||
*
|
||||
* @param changeSelectInScrolling true:更新、false:不更新
|
||||
*/
|
||||
public void setChangeSelectInScrolling(boolean changeSelectInScrolling) {
|
||||
mChangeSelectInScrolling = changeSelectInScrolling;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置滚动方向
|
||||
*
|
||||
* @param orientation 滚动方向
|
||||
* @return 最终的滚动方向
|
||||
*/
|
||||
@OrientationType
|
||||
public int setOrientationType(@OrientationType int orientation) {
|
||||
if (mOrientation == orientation || mScrollState != SCROLL_STATE_IDLE) return mOrientation;
|
||||
mOrientation = orientation;
|
||||
mItemFrames.clear();
|
||||
int x = mOffsetX;
|
||||
int y = mOffsetY;
|
||||
mOffsetX = y / getUsableHeight() * getUsableWidth();
|
||||
mOffsetY = x / getUsableWidth() * getUsableHeight();
|
||||
int mx = mMaxScrollX;
|
||||
int my = mMaxScrollY;
|
||||
mMaxScrollX = my / getUsableHeight() * getUsableWidth();
|
||||
mMaxScrollY = mx / getUsableWidth() * getUsableHeight();
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
//--- 滚动到指定位置 -----------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
|
||||
int targetPageIndex = getPageIndexByPos(position);
|
||||
smoothScrollToPage(targetPageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑滚动到上一页
|
||||
*/
|
||||
public void smoothPrePage() {
|
||||
smoothScrollToPage(getPageIndexByOffset() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑滚动到下一页
|
||||
*/
|
||||
public void smoothNextPage() {
|
||||
smoothScrollToPage(getPageIndexByOffset() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑滚动到指定页面
|
||||
*
|
||||
* @param pageIndex 页面下标
|
||||
*/
|
||||
public void smoothScrollToPage(int pageIndex) {
|
||||
if (pageIndex < 0 || pageIndex >= mLastPageCount) {
|
||||
Log.e(TAG, "pageIndex is outOfIndex, must in [0, " + mLastPageCount + ").");
|
||||
return;
|
||||
}
|
||||
if (null == mRecyclerView) {
|
||||
Log.e(TAG, "RecyclerView Not Found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果滚动到页面之间距离过大,先直接滚动到目标页面到临近页面,在使用 smoothScroll 最终滚动到目标
|
||||
// 否则在滚动距离很大时,会导致滚动耗费的时间非常长
|
||||
int currentPageIndex = getPageIndexByOffset();
|
||||
if (Math.abs(pageIndex - currentPageIndex) > 3) {
|
||||
if (pageIndex > currentPageIndex) {
|
||||
scrollToPage(pageIndex - 3);
|
||||
} else if (pageIndex < currentPageIndex) {
|
||||
scrollToPage(pageIndex + 3);
|
||||
}
|
||||
}
|
||||
|
||||
// 具体执行滚动
|
||||
LinearSmoothScroller smoothScroller = new PagerGridSmoothScroller(mRecyclerView);
|
||||
int position = pageIndex * mOnePageSize;
|
||||
smoothScroller.setTargetPosition(position);
|
||||
startSmoothScroll(smoothScroller);
|
||||
}
|
||||
|
||||
//=== 直接滚动 ===
|
||||
|
||||
@Override
|
||||
public void scrollToPosition(int position) {
|
||||
int pageIndex = getPageIndexByPos(position);
|
||||
scrollToPage(pageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上一页
|
||||
*/
|
||||
public void prePage() {
|
||||
scrollToPage(getPageIndexByOffset() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页
|
||||
*/
|
||||
public void nextPage() {
|
||||
scrollToPage(getPageIndexByOffset() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到指定页面
|
||||
*
|
||||
* @param pageIndex 页面下标
|
||||
*/
|
||||
public void scrollToPage(int pageIndex) {
|
||||
if (pageIndex < 0 || pageIndex >= mLastPageCount) {
|
||||
Log.e(TAG, "pageIndex = " + pageIndex + " is out of bounds, mast in [0, " + mLastPageCount + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if (null == mRecyclerView) {
|
||||
Log.e(TAG, "RecyclerView Not Found!");
|
||||
return;
|
||||
}
|
||||
|
||||
int mTargetOffsetXBy = 0;
|
||||
int mTargetOffsetYBy = 0;
|
||||
if (canScrollVertically()) {
|
||||
mTargetOffsetXBy = 0;
|
||||
mTargetOffsetYBy = pageIndex * getUsableHeight() - mOffsetY;
|
||||
} else {
|
||||
mTargetOffsetXBy = pageIndex * getUsableWidth() - mOffsetX;
|
||||
mTargetOffsetYBy = 0;
|
||||
}
|
||||
Loge("mTargetOffsetXBy = " + mTargetOffsetXBy);
|
||||
Loge("mTargetOffsetYBy = " + mTargetOffsetYBy);
|
||||
mRecyclerView.scrollBy(mTargetOffsetXBy, mTargetOffsetYBy);
|
||||
setPageIndex(pageIndex, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许连续滚动,默认为允许
|
||||
*
|
||||
* @return true 允许, false 不允许
|
||||
*/
|
||||
public boolean isAllowContinuousScroll() {
|
||||
return mAllowContinuousScroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否允许连续滚动
|
||||
*
|
||||
* @param allowContinuousScroll true 允许,false 不允许
|
||||
*/
|
||||
public void setAllowContinuousScroll(boolean allowContinuousScroll) {
|
||||
mAllowContinuousScroll = allowContinuousScroll;
|
||||
}
|
||||
|
||||
//--- 对外接口 ----------------------------------------------------------------------------------
|
||||
|
||||
private PageListener mPageListener = null;
|
||||
|
||||
public void setPageListener(PageListener pageListener) {
|
||||
mPageListener = pageListener;
|
||||
}
|
||||
|
||||
public interface PageListener {
|
||||
/**
|
||||
* 页面总数量变化
|
||||
*
|
||||
* @param pageSize 页面总数
|
||||
*/
|
||||
void onPageSizeChanged(int pageSize);
|
||||
|
||||
/**
|
||||
* 页面被选中
|
||||
*
|
||||
* @param pageIndex 选中的页面
|
||||
*/
|
||||
void onPageSelect(int pageIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.xscm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
|
||||
import static com.xscm.moduleutil.widget.pagerecyclerview.PagerConfig.Logi;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: 用于处理平滑滚动,用于用户手指抬起后页面对齐或者 Fling 事件
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:08
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:08
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerGridSmoothScroller extends LinearSmoothScroller {
|
||||
private RecyclerView mRecyclerView;
|
||||
|
||||
public PagerGridSmoothScroller(@NonNull RecyclerView recyclerView) {
|
||||
super(recyclerView.getContext());
|
||||
mRecyclerView = recyclerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
|
||||
RecyclerView.LayoutManager manager = mRecyclerView.getLayoutManager();
|
||||
if (null == manager) return;
|
||||
if (manager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager layoutManager = (PagerGridLayoutManager) manager;
|
||||
int pos = mRecyclerView.getChildAdapterPosition(targetView);
|
||||
int[] snapDistances = layoutManager.getSnapOffset(pos);
|
||||
final int dx = snapDistances[0];
|
||||
final int dy = snapDistances[1];
|
||||
Logi("dx = " + dx);
|
||||
Logi("dy = " + dy);
|
||||
final int time = calculateTimeForScrolling(Math.max(Math.abs(dx), Math.abs(dy)));
|
||||
if (time > 0) {
|
||||
action.update(dx, dy, time, mDecelerateInterpolator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
|
||||
return PagerConfig.getMillisecondsPreInch() / displayMetrics.densityDpi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.xscm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
|
||||
import static com.xscm.moduleutil.widget.pagerecyclerview.PagerConfig.Loge;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.SnapHelper;
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: 分页居中工具 每次只滚动一个页面
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:10
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:10
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerGridSnapHelper extends SnapHelper {
|
||||
private RecyclerView mRecyclerView; // RecyclerView
|
||||
|
||||
/**
|
||||
* 用于将滚动工具和 Recycler 绑定
|
||||
*
|
||||
* @param recyclerView RecyclerView
|
||||
* @throws IllegalStateException 状态异常
|
||||
*/
|
||||
@Override
|
||||
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws
|
||||
IllegalStateException {
|
||||
super.attachToRecyclerView(recyclerView);
|
||||
mRecyclerView = recyclerView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算需要滚动的向量,用于页面自动回滚对齐
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @param targetView 目标控件
|
||||
* @return 需要滚动的距离
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
|
||||
@NonNull View targetView) {
|
||||
int pos = layoutManager.getPosition(targetView);
|
||||
Loge("findTargetSnapPosition, pos = " + pos);
|
||||
int[] offset = new int[2];
|
||||
if (layoutManager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
|
||||
offset = manager.getSnapOffset(pos);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得需要对齐的View,对于分页布局来说,就是页面第一个
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @return 目标控件
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
|
||||
if (layoutManager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
|
||||
return manager.findSnapView();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标控件的位置下标
|
||||
* (获取滚动后第一个View的下标)
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @param velocityX X 轴滚动速率
|
||||
* @param velocityY Y 轴滚动速率
|
||||
* @return 目标控件的下标
|
||||
*/
|
||||
@Override
|
||||
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager,
|
||||
int velocityX, int velocityY) {
|
||||
int target = RecyclerView.NO_POSITION;
|
||||
Loge("findTargetSnapPosition, velocityX = " + velocityX + ", velocityY" + velocityY);
|
||||
if (null != layoutManager && layoutManager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
|
||||
if (manager.canScrollHorizontally()) {
|
||||
if (velocityX > PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findNextPageFirstPos();
|
||||
} else if (velocityX < -PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findPrePageFirstPos();
|
||||
}
|
||||
} else if (manager.canScrollVertically()) {
|
||||
if (velocityY > PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findNextPageFirstPos();
|
||||
} else if (velocityY < -PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findPrePageFirstPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loge("findTargetSnapPosition, target = " + target);
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一扔(快速滚动)
|
||||
*
|
||||
* @param velocityX X 轴滚动速率
|
||||
* @param velocityY Y 轴滚动速率
|
||||
* @return 是否消费该事件
|
||||
*/
|
||||
@Override
|
||||
public boolean onFling(int velocityX, int velocityY) {
|
||||
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
|
||||
if (layoutManager == null) {
|
||||
return false;
|
||||
}
|
||||
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
|
||||
if (adapter == null) {
|
||||
return false;
|
||||
}
|
||||
int minFlingVelocity = PagerConfig.getFlingThreshold();
|
||||
Loge("minFlingVelocity = " + minFlingVelocity);
|
||||
return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
|
||||
&& snapFromFling(layoutManager, velocityX, velocityY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速滚动的具体处理方案
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @param velocityX X 轴滚动速率
|
||||
* @param velocityY Y 轴滚动速率
|
||||
* @return 是否消费该事件
|
||||
*/
|
||||
private boolean snapFromFling(@NonNull RecyclerView.LayoutManager layoutManager, int velocityX,
|
||||
int velocityY) {
|
||||
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RecyclerView.SmoothScroller smoothScroller = createSnapScroller(layoutManager);
|
||||
if (smoothScroller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
|
||||
if (targetPosition == RecyclerView.NO_POSITION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
smoothScroller.setTargetPosition(targetPosition);
|
||||
layoutManager.startSmoothScroll(smoothScroller);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义 LinearSmoothScroller 来控制速度
|
||||
*
|
||||
* @param layoutManager 布局故哪里去
|
||||
* @return 自定义 LinearSmoothScroller
|
||||
*/
|
||||
protected LinearSmoothScroller createSnapScroller(RecyclerView.LayoutManager layoutManager) {
|
||||
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
|
||||
return null;
|
||||
}
|
||||
return new PagerGridSmoothScroller(mRecyclerView);
|
||||
}
|
||||
|
||||
//--- 公开方法 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 设置滚动阀值
|
||||
*
|
||||
* @param threshold 滚动阀值
|
||||
*/
|
||||
public void setFlingThreshold(int threshold) {
|
||||
PagerConfig.setFlingThreshold(threshold);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,730 @@
|
||||
package com.xscm.moduleutil.widget.picker;
|
||||
|
||||
|
||||
import static lombok.Lombok.checkNotNull;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Camera;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.text.Layout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.OverScroller;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.utils.logger.Logger;
|
||||
import com.xscm.moduleutil.widget.Utils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PickerView extends View {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
static final int DEFAULT_TEXT_SIZE_IN_SP = 14;
|
||||
static final int DEFAULT_ITEM_HEIGHT_IN_DP = 24;
|
||||
static final int DEFAULT_MAX_OFFSET_ITEM_COUNT = 3;
|
||||
private int preferredMaxOffsetItemCount = DEFAULT_MAX_OFFSET_ITEM_COUNT;
|
||||
private int selectedItemPosition;
|
||||
|
||||
private static final int DURATION_SHORT = 250;
|
||||
private static final int DURATION_LONG = 1000;
|
||||
|
||||
private Adapter<? extends PickerItem> adapter;
|
||||
|
||||
private Paint textPaint;
|
||||
private Rect textBounds = new Rect();
|
||||
|
||||
private GestureDetector gestureDetector;
|
||||
private OverScroller scroller;
|
||||
private boolean scrolling;
|
||||
private boolean pendingJustify;
|
||||
private boolean isScrollSuspendedByDownEvent;
|
||||
private float actionDownY;
|
||||
private float previousTouchedY;
|
||||
private int previousScrollerY;
|
||||
private int yOffset;
|
||||
private int minY;
|
||||
private int maxY;
|
||||
private int maxOverScrollY;
|
||||
private int touchSlop;
|
||||
|
||||
private int itemHeight;
|
||||
private int textSize;
|
||||
private int textColor = Color.BLACK;
|
||||
private int selectTextColor = Color.BLACK;
|
||||
private Typeface typeface;
|
||||
private boolean isCyclic;
|
||||
private boolean autoFitSize;
|
||||
private boolean curved;
|
||||
private Drawable selectedItemDrawable;
|
||||
private int selectedMaskPadding;
|
||||
private int[] DEFAULT_GRADIENT_COLORS = new int[]{0xcfffffff, 0x9fffffff, 0x5fffffff};
|
||||
private int[] gradientColors = DEFAULT_GRADIENT_COLORS;
|
||||
private GradientDrawable topMask;
|
||||
private GradientDrawable bottomMask;
|
||||
private Layout.Alignment textAlign = Layout.Alignment.ALIGN_CENTER;
|
||||
|
||||
private float radius;
|
||||
private Camera camera;
|
||||
private Matrix matrix;
|
||||
|
||||
public interface PickerItem {
|
||||
String getText();
|
||||
}
|
||||
|
||||
public PickerView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public PickerView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public PickerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
int startScrollerY = yOffset - itemHeight * selectedItemPosition;
|
||||
|
||||
if (startScrollerY <= minY || startScrollerY >= maxY) {
|
||||
justify(DURATION_LONG);
|
||||
return true;
|
||||
}
|
||||
|
||||
scroller.fling(
|
||||
0, startScrollerY,
|
||||
0, (int) velocityY,
|
||||
0, 0,
|
||||
minY,
|
||||
maxY,
|
||||
0, maxOverScrollY);
|
||||
|
||||
if (DEBUG) {
|
||||
Logger.d("fling: " + startScrollerY + ", velocityY: " + velocityY);
|
||||
}
|
||||
|
||||
previousScrollerY = scroller.getCurrY();
|
||||
pendingJustify = true;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
scroller = new OverScroller(getContext());
|
||||
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
if (isInEditMode()) {
|
||||
adapter = new Adapter<PickerItem>() {
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return getMaxCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PickerItem getItem(final int index) {
|
||||
return new PickerItem() {
|
||||
@Override
|
||||
public String getText() {
|
||||
return "Item " + index;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
} else {
|
||||
selectedMaskPadding = dp2px(18);
|
||||
selectedItemDrawable = Utils.getDrawable(getContext(), R.drawable.top_defaults_view_pickerview_selected_item);
|
||||
}
|
||||
|
||||
topMask = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, gradientColors);
|
||||
bottomMask = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, gradientColors);
|
||||
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PickerView);
|
||||
preferredMaxOffsetItemCount = typedArray.getInt(R.styleable.PickerView_preferredMaxOffsetItemCount, DEFAULT_MAX_OFFSET_ITEM_COUNT);
|
||||
if (preferredMaxOffsetItemCount <= 0)
|
||||
preferredMaxOffsetItemCount = DEFAULT_MAX_OFFSET_ITEM_COUNT;
|
||||
|
||||
int defaultItemHeight = Utils.pixelOfDp(getContext(), DEFAULT_ITEM_HEIGHT_IN_DP);
|
||||
itemHeight = typedArray.getDimensionPixelSize(R.styleable.PickerView_itemHeight, defaultItemHeight);
|
||||
if (itemHeight <= 0) itemHeight = defaultItemHeight;
|
||||
|
||||
int defaultTextSize = Utils.pixelOfScaled(getContext(), DEFAULT_TEXT_SIZE_IN_SP);
|
||||
textSize = typedArray.getDimensionPixelSize(R.styleable.PickerView_textSizec, defaultTextSize);
|
||||
textColor = typedArray.getColor(R.styleable.PickerView_textColorc, Color.BLACK);
|
||||
selectTextColor = typedArray.getColor(R.styleable.PickerView_selectTextColor, Color.BLACK);
|
||||
|
||||
isCyclic = typedArray.getBoolean(R.styleable.PickerView_isCyclic, false);
|
||||
autoFitSize = typedArray.getBoolean(R.styleable.PickerView_autoFitSize, true);
|
||||
curved = typedArray.getBoolean(R.styleable.PickerView_curved, false);
|
||||
typedArray.recycle();
|
||||
|
||||
initPaints();
|
||||
|
||||
camera = new Camera();
|
||||
matrix = new Matrix();
|
||||
}
|
||||
|
||||
protected int dp2px(float dp) {
|
||||
final float scale = getResources().getDisplayMetrics().density;
|
||||
return (int) (dp * scale + 0.5f);
|
||||
}
|
||||
|
||||
private void initPaints() {
|
||||
textPaint = new Paint();
|
||||
textPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
public Adapter getAdapter() {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public <T extends PickerItem> void setAdapter(final Adapter<T> adapter) {
|
||||
checkNotNull(adapter, "adapter == null");
|
||||
if (adapter.getItemCount() > Integer.MAX_VALUE / itemHeight) {
|
||||
throw new RuntimeException("getItemCount() is too large, max count can be PickerView.getMaxCount()");
|
||||
}
|
||||
adapter.setPickerView(this);
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
public interface OnItemSelectedListener<T extends PickerItem> {
|
||||
void onItemSelected(T item);
|
||||
}
|
||||
|
||||
public <T extends PickerItem> void setItems(final List<T> items, final OnItemSelectedListener<T> listener) {
|
||||
final Adapter<T> simpleAdapter = new Adapter<T>() {
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getItem(int index) {
|
||||
return items.get(index);
|
||||
}
|
||||
};
|
||||
setAdapter(simpleAdapter);
|
||||
|
||||
setOnSelectedItemChangedListener(new OnSelectedItemChangedListener() {
|
||||
@Override
|
||||
public void onSelectedItemChanged(PickerView pickerView, int previousPosition, int selectedItemPosition) {
|
||||
if (listener != null) {
|
||||
listener.onItemSelected(simpleAdapter.getItem(selectedItemPosition));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected int getMaxCount() {
|
||||
return Integer.MAX_VALUE / itemHeight;
|
||||
}
|
||||
|
||||
public void notifyDataSetChanged() {
|
||||
if (adapter != null) {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSelectedItem() {
|
||||
float centerPosition = centerPosition();
|
||||
int newSelectedItemPosition = (int) Math.floor(centerPosition);
|
||||
notifySelectedItemChangedIfNeeded(newSelectedItemPosition, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public abstract static class Adapter<T extends PickerItem> {
|
||||
private WeakReference<PickerView> pickerViewRef;
|
||||
|
||||
private void setPickerView(PickerView pickerView) {
|
||||
this.pickerViewRef = new WeakReference<>(pickerView);
|
||||
}
|
||||
|
||||
public void notifyDataSetChanged() {
|
||||
if (pickerViewRef != null) {
|
||||
PickerView pickerView = pickerViewRef.get();
|
||||
if (pickerView != null) {
|
||||
pickerView.updateSelectedItem();
|
||||
pickerView.computeScrollParams();
|
||||
if (!pickerView.scroller.isFinished()) {
|
||||
pickerView.scroller.forceFinished(true);
|
||||
}
|
||||
pickerView.justify(0);
|
||||
pickerView.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract int getItemCount();
|
||||
|
||||
public abstract T getItem(int index);
|
||||
|
||||
public T getLastItem() {
|
||||
return getItem(getItemCount() - 1);
|
||||
}
|
||||
|
||||
public String getText(int index) {
|
||||
if (getItem(index) == null) return "null";
|
||||
return getItem(index).getText();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPreferredMaxOffsetItemCount(int preferredMaxOffsetItemCount) {
|
||||
this.preferredMaxOffsetItemCount = preferredMaxOffsetItemCount;
|
||||
}
|
||||
|
||||
public void setItemHeight(int itemHeight) {
|
||||
if (this.itemHeight != itemHeight) {
|
||||
this.itemHeight = itemHeight;
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextSize(int textSize) {
|
||||
if (this.textSize != textSize) {
|
||||
this.textSize = textSize;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextColor(int textColor) {
|
||||
if (this.textColor != textColor) {
|
||||
this.textColor = textColor;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTypeface(Typeface typeface) {
|
||||
if (this.typeface != typeface) {
|
||||
this.typeface = typeface;
|
||||
textPaint.setTypeface(typeface);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCyclic(boolean cyclic) {
|
||||
if (this.isCyclic != cyclic) {
|
||||
isCyclic = cyclic;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setAutoFitSize(boolean autoFitSize) {
|
||||
if (this.autoFitSize != autoFitSize) {
|
||||
this.autoFitSize = autoFitSize;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurved(boolean curved) {
|
||||
if (this.curved != curved) {
|
||||
this.curved = curved;
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public int getSelectedItemPosition() {
|
||||
return clampItemPosition(selectedItemPosition);
|
||||
}
|
||||
|
||||
public void setSelectedItemPosition(int selectedItemPosition) {
|
||||
checkNotNull(adapter, "adapter must be set first");
|
||||
|
||||
notifySelectedItemChangedIfNeeded(selectedItemPosition);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public <T extends PickerItem> T getSelectedItem(Class<T> cls) {
|
||||
checkNotNull(adapter, "adapter must be set first");
|
||||
|
||||
PickerItem item = adapter.getItem(getSelectedItemPosition());
|
||||
if (!cls.isInstance(item)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return cls.cast(item);
|
||||
}
|
||||
|
||||
public interface OnSelectedItemChangedListener {
|
||||
void onSelectedItemChanged(PickerView pickerView, int previousPosition, int selectedItemPosition);
|
||||
}
|
||||
|
||||
private OnSelectedItemChangedListener onSelectedItemChangedListener;
|
||||
|
||||
public void setOnSelectedItemChangedListener(OnSelectedItemChangedListener onSelectedItemChangedListener) {
|
||||
this.onSelectedItemChangedListener = onSelectedItemChangedListener;
|
||||
}
|
||||
|
||||
private void notifySelectedItemChangedIfNeeded(int newSelectedItemPosition) {
|
||||
notifySelectedItemChangedIfNeeded(newSelectedItemPosition, false);
|
||||
}
|
||||
|
||||
private void notifySelectedItemChangedIfNeeded(int newSelectedItemPosition, boolean forceNotify) {
|
||||
int oldSelectedItemPosition = selectedItemPosition;
|
||||
int clampedNewSelectedItemPosition = clampItemPosition(newSelectedItemPosition);
|
||||
|
||||
boolean changed = forceNotify;
|
||||
if (isCyclic) {
|
||||
if (selectedItemPosition != newSelectedItemPosition) {
|
||||
selectedItemPosition = newSelectedItemPosition;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if (selectedItemPosition != clampedNewSelectedItemPosition) {
|
||||
selectedItemPosition = clampedNewSelectedItemPosition;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed && onSelectedItemChangedListener != null) {
|
||||
onSelectedItemChangedListener.onSelectedItemChanged(this, oldSelectedItemPosition, clampedNewSelectedItemPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
checkNotNull(adapter, "adapter == null");
|
||||
|
||||
int height = resolveSizeAndState(calculateIntrinsicHeight(), heightMeasureSpec, 0);
|
||||
computeScrollParams();
|
||||
setMeasuredDimension(widthMeasureSpec, height);
|
||||
}
|
||||
|
||||
private int calculateIntrinsicHeight() {
|
||||
if (curved) {
|
||||
radius = itemHeight / (float) Math.sin(Math.PI / (3 + 2 * preferredMaxOffsetItemCount));
|
||||
return (int) Math.ceil(2 * radius);
|
||||
} else {
|
||||
return (1 + 2 * preferredMaxOffsetItemCount) * itemHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
checkNotNull(adapter, "adapter == null");
|
||||
if (adapter.getItemCount() == 0 || itemHeight == 0) return;
|
||||
|
||||
if (!isInEditMode()) {
|
||||
selectedItemDrawable.setBounds(selectedMaskPadding, (getMeasuredHeight() - itemHeight) / 2, getMeasuredWidth() - selectedMaskPadding, (getMeasuredHeight() + itemHeight) / 2);
|
||||
selectedItemDrawable.draw(canvas);
|
||||
}
|
||||
|
||||
drawItems(canvas);
|
||||
drawMasks(canvas);
|
||||
}
|
||||
|
||||
private void drawItems(Canvas canvas) {
|
||||
// 绘制选中项
|
||||
float drawYOffset = yOffset + (getMeasuredHeight() - itemHeight) / 2;
|
||||
int itemPosition = selectedItemPosition;
|
||||
String text = adapter.getText(clampItemPosition(itemPosition));
|
||||
drawText(canvas, text, drawYOffset, 0);
|
||||
drawYOffset -= itemHeight;
|
||||
|
||||
// 绘制选中项上方的item
|
||||
itemPosition = selectedItemPosition - 1;
|
||||
while (drawYOffset + (itemHeight * (curved ? 2 : 1)) > 0) {
|
||||
if (isPositionInvalid(itemPosition) && !isCyclic) {
|
||||
break;
|
||||
}
|
||||
|
||||
text = adapter.getText(clampItemPosition(itemPosition));
|
||||
drawText(canvas, text, drawYOffset, -1);
|
||||
drawYOffset -= itemHeight;
|
||||
itemPosition--;
|
||||
}
|
||||
|
||||
// 绘制选中项下方的item
|
||||
drawYOffset = yOffset + (getMeasuredHeight() + itemHeight) / 2;
|
||||
itemPosition = selectedItemPosition + 1;
|
||||
while (drawYOffset - (itemHeight * (curved ? 1 : 0)) < getMeasuredHeight()) {
|
||||
if (isPositionInvalid(itemPosition) && !isCyclic) {
|
||||
break;
|
||||
}
|
||||
|
||||
text = adapter.getText(clampItemPosition(itemPosition));
|
||||
drawText(canvas, text, drawYOffset, 1);
|
||||
drawYOffset += itemHeight;
|
||||
itemPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
private void drawMasks(Canvas canvas) {
|
||||
topMask.setBounds(0, 0, getMeasuredWidth(), (getMeasuredHeight() - itemHeight) / 2);
|
||||
topMask.draw(canvas);
|
||||
|
||||
bottomMask.setBounds(0, (getMeasuredHeight() + itemHeight) / 2, getMeasuredWidth(), getMeasuredHeight());
|
||||
bottomMask.draw(canvas);
|
||||
}
|
||||
|
||||
private void drawText(Canvas canvas, String text, float offset, int postion) {
|
||||
textPaint.setTextSize(textSize);
|
||||
if (postion == 0) {
|
||||
textPaint.setColor(selectTextColor);
|
||||
} else {
|
||||
textPaint.setColor(textColor);
|
||||
}
|
||||
textPaint.getTextBounds(text, 0, text.length(), textBounds);
|
||||
|
||||
if (autoFitSize) {
|
||||
while (getMeasuredWidth() < textBounds.width() && textPaint.getTextSize() > 16) {
|
||||
textPaint.setTextSize(textPaint.getTextSize() - 1);
|
||||
textPaint.getTextBounds(text, 0, text.length(), textBounds);
|
||||
}
|
||||
}
|
||||
|
||||
float textBottom = offset + (itemHeight + (textBounds.height())) / 2;
|
||||
|
||||
if (curved) {
|
||||
// 根据当前item的offset换算得到对应的倾斜角度,rotateRatio用于减小倾斜角度,否则倾斜角度过大会导致视觉效果不佳
|
||||
float rotateRatio = 2f / preferredMaxOffsetItemCount;
|
||||
double radian = Math.atan((radius - (offset + itemHeight / 2)) / radius) * rotateRatio;
|
||||
float degree = (float) (radian * 180 / Math.PI);
|
||||
camera.save();
|
||||
camera.rotateX(degree);
|
||||
camera.translate(0, 0, -Math.abs((radius / (2 + preferredMaxOffsetItemCount)) * (float) Math.sin(radian)));
|
||||
camera.getMatrix(matrix);
|
||||
matrix.preTranslate(-getMeasuredWidth() / 2, -getMeasuredHeight() / 2);
|
||||
matrix.postTranslate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
|
||||
canvas.save();
|
||||
canvas.concat(matrix);
|
||||
}
|
||||
if (textAlign == Layout.Alignment.ALIGN_CENTER) {
|
||||
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||
canvas.drawText(text, getMeasuredWidth() / 2, textBottom, textPaint);
|
||||
} else if (textAlign == Layout.Alignment.ALIGN_OPPOSITE) {
|
||||
textPaint.setTextAlign(Paint.Align.RIGHT);
|
||||
canvas.drawText(text, getMeasuredWidth(), textBottom, textPaint);
|
||||
} else {
|
||||
textPaint.setTextAlign(Paint.Align.LEFT);
|
||||
canvas.drawText(text, 0, textBottom, textPaint);
|
||||
}
|
||||
if (curved) {
|
||||
canvas.restore();
|
||||
camera.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performClick() {
|
||||
return super.performClick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (gestureDetector.onTouchEvent(event)) {
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
float y = event.getY();
|
||||
int dy;
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
pendingJustify = false;
|
||||
actionDownY = y;
|
||||
previousTouchedY = y;
|
||||
scrolling = false;
|
||||
if (!scroller.isFinished()) {
|
||||
scroller.forceFinished(true);
|
||||
isScrollSuspendedByDownEvent = true;
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (!scrolling && Math.abs(y - actionDownY) <= touchSlop) {
|
||||
break;
|
||||
}
|
||||
if (!scrolling) {
|
||||
scrolling = true;
|
||||
previousTouchedY = y;
|
||||
break;
|
||||
}
|
||||
pendingJustify = false;
|
||||
dy = (int) (y - previousTouchedY);
|
||||
handleOffset(dy);
|
||||
previousTouchedY = y;
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (!isScrollSuspendedByDownEvent && !scrolling && Math.abs(y - actionDownY) <= touchSlop) {
|
||||
// 单击事件
|
||||
performClick();
|
||||
previousScrollerY = yOffset - itemHeight * selectedItemPosition;
|
||||
// 最近的整数倍数的单元格高度
|
||||
int centerItemTopY = (getMeasuredHeight() - itemHeight) / 2;
|
||||
int centerItemBottomY = (getMeasuredHeight() + itemHeight) / 2;
|
||||
if (y >= centerItemTopY && y <= centerItemBottomY) break;
|
||||
|
||||
int scrollOffset;
|
||||
if (y < centerItemTopY) {
|
||||
scrollOffset = ((int) y - centerItemBottomY) / itemHeight * itemHeight;
|
||||
if (selectedItemPosition + scrollOffset / itemHeight < 0) break;
|
||||
} else {
|
||||
scrollOffset = ((int) y - centerItemTopY) / itemHeight * itemHeight;
|
||||
if (selectedItemPosition + scrollOffset / itemHeight > adapter.getItemCount() - 1)
|
||||
break;
|
||||
}
|
||||
scroller.startScroll(
|
||||
0, previousScrollerY,
|
||||
0, -scrollOffset,
|
||||
DURATION_SHORT);
|
||||
if (DEBUG) {
|
||||
Logger.d("scrollOffset = %d", scrollOffset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
scrolling = false;
|
||||
isScrollSuspendedByDownEvent = false;
|
||||
|
||||
// align items
|
||||
dy = (int) (y - previousTouchedY);
|
||||
handleOffset(dy);
|
||||
justify(DURATION_SHORT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeScroll() {
|
||||
if (scroller.computeScrollOffset()) {
|
||||
int scrollerY = scroller.getCurrY();
|
||||
if (DEBUG) {
|
||||
Logger.d("scrollerY = %d, previousScrollerY = %d", scrollerY, previousScrollerY);
|
||||
}
|
||||
int dy = scrollerY - previousScrollerY;
|
||||
handleOffset(dy);
|
||||
previousScrollerY = scrollerY;
|
||||
invalidate();
|
||||
} else {
|
||||
if (pendingJustify) {
|
||||
justify(DURATION_SHORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void computeScrollParams() {
|
||||
if (isCyclic) {
|
||||
minY = Integer.MIN_VALUE;
|
||||
maxY = Integer.MAX_VALUE;
|
||||
} else {
|
||||
minY = -(adapter.getItemCount() - 1) * itemHeight;
|
||||
maxY = 0;
|
||||
}
|
||||
maxOverScrollY = 2 * itemHeight;
|
||||
}
|
||||
|
||||
private boolean isPositionInvalid(int itemPosition) {
|
||||
return itemPosition < 0 || itemPosition >= adapter.getItemCount();
|
||||
}
|
||||
|
||||
private int clampItemPosition(int itemPosition) {
|
||||
if (adapter.getItemCount() == 0) return 0;
|
||||
|
||||
if (isCyclic) {
|
||||
if (itemPosition < 0) {
|
||||
itemPosition = itemPosition % adapter.getItemCount();
|
||||
if (itemPosition != 0) itemPosition += adapter.getItemCount();
|
||||
} else if (itemPosition >= adapter.getItemCount())
|
||||
itemPosition %= adapter.getItemCount();
|
||||
}
|
||||
|
||||
if (itemPosition < 0) itemPosition = 0;
|
||||
else if (itemPosition >= adapter.getItemCount()) itemPosition = adapter.getItemCount() - 1;
|
||||
return itemPosition;
|
||||
}
|
||||
|
||||
// 中心线切割的单元位置
|
||||
private float centerPosition() {
|
||||
return selectedItemPosition + 0.5f - yOffset / itemHeight;
|
||||
}
|
||||
|
||||
// 对齐item
|
||||
private void justify(int duration) {
|
||||
if (yOffset != 0) {
|
||||
int scrollOffset = -yOffset;
|
||||
if (selectedItemPosition != 0 && selectedItemPosition != adapter.getItemCount() - 1) {
|
||||
if (yOffset > 0) {
|
||||
if (yOffset > itemHeight / 3) {
|
||||
scrollOffset = itemHeight - yOffset;
|
||||
}
|
||||
} else {
|
||||
if (Math.abs(yOffset) > itemHeight / 3) {
|
||||
scrollOffset = -(itemHeight + yOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果item数量为1,总是回到0偏移
|
||||
if (adapter.getItemCount() > 1) {
|
||||
if (selectedItemPosition == 0 && yOffset < 0) {
|
||||
if (Math.abs(yOffset) > itemHeight / 3) {
|
||||
scrollOffset = -(itemHeight + yOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedItemPosition == adapter.getItemCount() - 1 && yOffset > 0) {
|
||||
if (yOffset > itemHeight / 3) {
|
||||
scrollOffset = itemHeight - yOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousScrollerY = yOffset - itemHeight * selectedItemPosition;
|
||||
scroller.startScroll(
|
||||
0, previousScrollerY,
|
||||
0, scrollOffset,
|
||||
duration);
|
||||
if (DEBUG) {
|
||||
Logger.d("justify: duration = %d, yOffset = %d, scrollOffset = %d", duration, yOffset, scrollOffset);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
pendingJustify = false;
|
||||
}
|
||||
|
||||
private void handleOffset(int dy) {
|
||||
if (DEBUG) {
|
||||
Logger.d("yOffset = %d, dy = %d", yOffset, dy);
|
||||
}
|
||||
yOffset += dy;
|
||||
|
||||
if (Math.abs(yOffset) >= itemHeight) {
|
||||
// 滚动到边界时
|
||||
if (selectedItemPosition == 0 && dy >= 0 || selectedItemPosition == adapter.getItemCount() - 1 && dy <= 0) {
|
||||
if (Math.abs(yOffset) > maxOverScrollY) {
|
||||
yOffset = yOffset > 0 ? maxOverScrollY : -maxOverScrollY;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int preSelection = selectedItemPosition;
|
||||
notifySelectedItemChangedIfNeeded(selectedItemPosition - (yOffset / itemHeight));
|
||||
yOffset -= (preSelection - selectedItemPosition) * itemHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.xscm.moduleutil.widget.picker;
|
||||
|
||||
import static com.xscm.moduleutil.utils.UtilConfig.getContext;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.luck.picture.lib.engine.CropFileEngine;
|
||||
import com.luck.picture.lib.style.BottomNavBarStyle;
|
||||
import com.luck.picture.lib.style.PictureSelectorStyle;
|
||||
import com.luck.picture.lib.style.SelectMainStyle;
|
||||
import com.luck.picture.lib.style.TitleBarStyle;
|
||||
import com.luck.picture.lib.utils.DensityUtil;
|
||||
import com.luck.picture.lib.utils.StyleUtils;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.yalantis.ucrop.UCrop;
|
||||
import com.yalantis.ucrop.UCropImageEngine;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @Author lxj$
|
||||
* @Time 2025-8-5 21:50:02$ $
|
||||
* @Description 图片选择器$
|
||||
*/
|
||||
public class PictureSelectorUtil {
|
||||
|
||||
public CropFileEngine ImageFileCropEngine;
|
||||
private PictureSelectorStyle selectorStyle;
|
||||
|
||||
public PictureSelectorStyle syltPictureSelector() {
|
||||
selectorStyle = new PictureSelectorStyle();
|
||||
// 主体风格
|
||||
SelectMainStyle numberSelectMainStyle = new SelectMainStyle();
|
||||
numberSelectMainStyle.setSelectNumberStyle(true);
|
||||
numberSelectMainStyle.setPreviewSelectNumberStyle(false);
|
||||
numberSelectMainStyle.setPreviewDisplaySelectGallery(true);
|
||||
numberSelectMainStyle.setSelectBackground(com.luck.picture.lib.R.drawable.ps_default_num_selector);
|
||||
numberSelectMainStyle.setPreviewSelectBackground(com.luck.picture.lib.R.drawable.ps_preview_checkbox_selector);
|
||||
numberSelectMainStyle.setSelectNormalBackgroundResources(com.luck.picture.lib.R.drawable.ps_select_complete_normal_bg);
|
||||
numberSelectMainStyle.setSelectNormalTextColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_53575e));
|
||||
numberSelectMainStyle.setSelectNormalText(com.luck.picture.lib.R.string.ps_send);
|
||||
numberSelectMainStyle.setAdapterPreviewGalleryBackgroundResource(com.luck.picture.lib.R.drawable.ps_preview_gallery_bg);
|
||||
numberSelectMainStyle.setAdapterPreviewGalleryItemSize(DensityUtil.dip2px(getContext(), 52));
|
||||
numberSelectMainStyle.setPreviewSelectText(com.luck.picture.lib.R.string.ps_select);
|
||||
numberSelectMainStyle.setPreviewSelectTextSize(14);
|
||||
numberSelectMainStyle.setPreviewSelectTextColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_white));
|
||||
numberSelectMainStyle.setPreviewSelectMarginRight(DensityUtil.dip2px(getContext(), 6));
|
||||
numberSelectMainStyle.setSelectBackgroundResources(com.luck.picture.lib.R.drawable.ps_select_complete_bg);
|
||||
numberSelectMainStyle.setSelectText(com.luck.picture.lib.R.string.ps_send_num);
|
||||
numberSelectMainStyle.setSelectTextColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_white));
|
||||
numberSelectMainStyle.setMainListBackgroundColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_black));
|
||||
numberSelectMainStyle.setCompleteSelectRelativeTop(true);
|
||||
numberSelectMainStyle.setPreviewSelectRelativeBottom(true);
|
||||
numberSelectMainStyle.setAdapterItemIncludeEdge(false);
|
||||
|
||||
// 头部TitleBar 风格
|
||||
TitleBarStyle numberTitleBarStyle = new TitleBarStyle();
|
||||
numberTitleBarStyle.setHideCancelButton(true);
|
||||
numberTitleBarStyle.setAlbumTitleRelativeLeft(true);
|
||||
numberTitleBarStyle.setTitleAlbumBackgroundResource(com.luck.picture.lib.R.drawable.ps_album_bg);
|
||||
numberTitleBarStyle.setTitleDrawableRightResource(com.luck.picture.lib.R.drawable.ps_ic_grey_arrow);
|
||||
numberTitleBarStyle.setPreviewTitleLeftBackResource(com.luck.picture.lib.R.drawable.ps_ic_normal_back);
|
||||
|
||||
// 底部NavBar 风格
|
||||
BottomNavBarStyle numberBottomNavBarStyle = new BottomNavBarStyle();
|
||||
numberBottomNavBarStyle.setBottomPreviewNarBarBackgroundColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_half_grey));
|
||||
numberBottomNavBarStyle.setBottomPreviewNormalText(com.luck.picture.lib.R.string.ps_preview);
|
||||
numberBottomNavBarStyle.setBottomPreviewNormalTextColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_9b));
|
||||
numberBottomNavBarStyle.setBottomPreviewNormalTextSize(16);
|
||||
numberBottomNavBarStyle.setCompleteCountTips(false);
|
||||
numberBottomNavBarStyle.setBottomPreviewSelectText(com.luck.picture.lib.R.string.ps_preview_num);
|
||||
numberBottomNavBarStyle.setBottomPreviewSelectTextColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_white));
|
||||
|
||||
|
||||
selectorStyle.setTitleBarStyle(numberTitleBarStyle);
|
||||
selectorStyle.setBottomBarStyle(numberBottomNavBarStyle);
|
||||
selectorStyle.setSelectMainStyle(numberSelectMainStyle);
|
||||
return selectorStyle;
|
||||
}
|
||||
|
||||
public class ImageFileCropEngine implements CropFileEngine {
|
||||
|
||||
@Override
|
||||
public void onStartCrop(Fragment fragment, Uri srcUri, Uri destinationUri, ArrayList<String> dataSource, int requestCode) {
|
||||
UCrop.Options options = buildOptions();
|
||||
UCrop uCrop = UCrop.of(srcUri, destinationUri, dataSource);
|
||||
uCrop.withOptions(options);
|
||||
uCrop.setImageEngine(new UCropImageEngine() {
|
||||
@Override
|
||||
public void loadImage(Context context, String url, ImageView imageView) {
|
||||
// if (!ImageLoaderUtils.assertValidRequest(context)) {
|
||||
// return;
|
||||
// }
|
||||
Glide.with(context).load(url).override(180, 180).into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadImage(Context context, Uri url, int maxWidth, int maxHeight, OnCallbackListener<Bitmap> call) {
|
||||
Glide.with(context).asBitmap().load(url).override(maxWidth, maxHeight).into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
||||
if (call != null) {
|
||||
call.onCall(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
if (call != null) {
|
||||
call.onCall(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
uCrop.start(fragment.requireActivity(), fragment, requestCode);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 配制UCrop,可根据需求自我扩展
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public UCrop.Options buildOptions() {
|
||||
UCrop.Options options = new UCrop.Options();
|
||||
options.setHideBottomControls(true);//是否显示裁剪
|
||||
options.setFreeStyleCropEnabled(true);//裁剪是否可以拖动
|
||||
options.setShowCropFrame(true);//是否显示裁剪边缘
|
||||
options.setShowCropGrid(true);//是否显示裁剪网格
|
||||
options.setCircleDimmedLayer(false);//圆形头像裁剪
|
||||
options.withAspectRatio(1, 1);
|
||||
options.isCropDragSmoothToCenter(false);
|
||||
options.isForbidCropGifWebp(false);
|
||||
options.isForbidSkipMultipleCrop(true);
|
||||
options.setMaxScaleMultiplier(100);
|
||||
if (selectorStyle != null && selectorStyle.getSelectMainStyle().getStatusBarColor() != 0) {
|
||||
SelectMainStyle mainStyle = selectorStyle.getSelectMainStyle();
|
||||
boolean isDarkStatusBarBlack = mainStyle.isDarkStatusBarBlack();
|
||||
int statusBarColor = mainStyle.getStatusBarColor();
|
||||
options.isDarkStatusBarBlack(isDarkStatusBarBlack);
|
||||
if (StyleUtils.checkStyleValidity(statusBarColor)) {
|
||||
options.setStatusBarColor(statusBarColor);
|
||||
options.setToolbarColor(statusBarColor);
|
||||
} else {
|
||||
options.setStatusBarColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_grey));
|
||||
options.setToolbarColor(ContextCompat.getColor(getContext(),com.luck.picture.lib.R.color.ps_color_grey));
|
||||
}
|
||||
TitleBarStyle titleBarStyle = selectorStyle.getTitleBarStyle();
|
||||
if (StyleUtils.checkStyleValidity(titleBarStyle.getTitleTextColor())) {
|
||||
options.setToolbarWidgetColor(titleBarStyle.getTitleTextColor());
|
||||
} else {
|
||||
options.setToolbarWidgetColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_white));
|
||||
}
|
||||
} else {
|
||||
options.setStatusBarColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_grey));
|
||||
options.setToolbarColor(ContextCompat.getColor(getContext(), com.luck.picture.lib.R.color.ps_color_grey));
|
||||
options.setToolbarWidgetColor(ContextCompat.getColor(getContext(),com.luck.picture.lib. R.color.ps_color_white));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user