Android自定义半圆形圆盘滚动选择器

前段时间公司项目要求做一个特效的滑动选择器,效果如下图的样子:

img

功能要求:两边的半圆形转盘可以转动,转盘上的图标也一起滚动,蓝红色图标指着的小图标变成高亮选中状态。

第一眼看到这个需求就想到这个必须要用自定义控件来做才行,于是产生了这样的思路:

半圆形的滚动的转盘自定义view继承viewgroup,重写滑动事件,自定义圆盘上图片的摆放角度,至于蓝色和红色箭头图标指向的选中状态可以用坐标数组绘制一个区域来判断是否有符合条件的图标滚动到了这个位置,如果有的话就将这个图标所在的控件透明度设置为1,如果没到这个位置就设置为非选中状态0.5透明度 ,思路这样定下来了,预计可以行得通,于是开始进行实际的尝试写代码实现这个自定义的控件和功能。

下面我直接把核心代码附上,注释比较清晰:

attrs.xml文件代码:

<!--自定义半圆形展示效果转盘选择器控件-- 
 <declare-styleable name="ringview_half" 
 <attr name="image_angle_rh" format="integer" / 
 <attr name="image_padding_rh" format="integer" / 
 <attr name="max_speed_rh" format="integer" / 
 <attr name="min_speed_rh" format="integer" / 
 <attr name="list_rh" format="integer" / 
 <attr name="can_scroll_rh" format="boolean" / 
 <attr name="is_right_select_icon_rh" format="boolean" / 
</declare-styleable 

自定义控件的类代码:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.wj.R;
import com.wj.utils.DensityUtil;
import com.wj.utils.ScreenUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @time 2018/6/8
* @author JunJieW
* @since 1376881525@qq.com
* @description 自定义半圆形展示效果转盘选择器控件
*/
public class RingViewHalf extends ViewGroup {
/**
* 上一次滑动的坐标
*/
private float mLastX;
private float mLastY;
/**
* 检测按下到抬起时使用的时间
*/
private long mDownTime;
/**
* 自动滚动线程
*/
private ScrollResetRunnable mScrollResetRunnable;
/**
* 检测按下到抬起时旋转的角度
*/
private float mTmpAngle;
/**
* 每秒最大移动角度
*/
private int mMax_Speed;
/**
* 如果移动角度达到该值,则屏蔽点击
*/
private int mMin_Speed;
/**
* 圆的直径
*/
private int mRadius;
/**
* 判断是否正在自动滚动
*/
private boolean isMove;
/**
* 布局滚动角度
*/
private int mStartAngle = 0;
/**
* 中间条的宽度
*/
private int mCircleLineStrokeWidth;
/**
* 图片内容偏移角度
*/
private int mImageAngle;
/**
* 是否初始化布局
*/
private boolean isChekc = false;
/**
* 布局view
*/
private List<Integer  mImageList = new ArrayList< ();
/**
* 是否可点击
*/
private boolean isCanClick = true;
/**
* 图片与环之间的padding
*/
private int mPadding;
/**
* 是否是右边居中的图标为选中图标
*/
private boolean is_right_select_icon = true;
/**
* 是否是右边居中的图标为选中图标
*/
private Rect select_icon_rect = new Rect();
//是否能转动
private boolean mCanScrool;
public RingViewHalf(Context context) {
this(context, null, 0);
}
public RingViewHalf(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RingViewHalf(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义控件设置的值
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ringview_half, 0, 0);
mMax_Speed = array.getInteger(R.styleable.ringview_half_max_speed_rh, 300);
mMin_Speed = array.getInteger(R.styleable.ringview_half_min_speed_rh, 3);
mImageAngle = array.getInteger(R.styleable.ringview_half_image_angle_rh, 0);
mPadding = array.getInteger(R.styleable.ringview_half_image_padding_rh, 0);
mCanScrool = array.getBoolean(R.styleable.ringview_half_can_scroll_rh, true);
is_right_select_icon = array.getBoolean(R.styleable.ringview_half_is_right_select_icon_rh, true);
//获取xml定义的资源文件
TypedArray mList = context.getResources().obtainTypedArray(array.getResourceId(R.styleable.ringview_half_list_rh, 0));
int len = mList.length();
if (len   0) {
for (int i = 0; i < len; i++)
mImageList.add(mList.getResourceId(i, 0));
} else {
mImageList.add(R.mipmap.icon);
mImageList.add(R.mipmap.icon);
mImageList.add(R.mipmap.icon);
}
mList.recycle();
array.recycle();
int [] location =new int [2];
getLocationInWindow(location);
Log.d("locationInWindow","    X=="+location[0]+"y=="+location[1]);
addImgIcon();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (!isChekc) {
initView();
mRadius = getWidth();
isChekc = true;
}
}
/**
* 测量
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = this.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
child.getMeasuredWidth();
}
}
/**
* 排版布局
*/
private void initView() {
int width = this.getWidth();
int height = this.getHeight();
if (width != height) {
int min = Math.min(width, height);
width = min;
height = min;
}
//不同屏幕分辨率下做不同的处理
float instPadding = 70f;
if (ScreenUtils.getScreenWidth(getContext())<=720){
instPadding = 55f;
}
//图片摆放的圆弧半径
mCircleLineStrokeWidth = getChildAt(0).getMeasuredHeight() + DensityUtil.dip2px(getContext(),instPadding) + mPadding;
//计算图片圆的半径
final int mContent = width / 2 - mCircleLineStrokeWidth / 2;
for (int i = 0; i < getChildCount(); i++) {
View child = this.getChildAt(i);
//计算每个图片摆放的角度
int mAnGle = 360 / mImageList.size() * (i + 1) + mImageAngle;
//获取每个图片摆放的左上角的x和y坐标
float left = (float) (width / 2 + mContent * Math.cos(mAnGle * Math.PI / 180)) - child.getMeasuredWidth() / 2;
float top = (float) (height / 2 + mContent * Math.sin(mAnGle * Math.PI / 180)) - child.getMeasuredHeight() / 2;
/**
* 一四象限
*/
if (getQuadrantByAngle(mAnGle) == 1 || getQuadrantByAngle(mAnGle) == 4) {
// child.setRotation(mAnGle - 270);
/**
* 二三象限
*/
} else {
// child.setRotation(mAnGle + 90);
}
child.layout((int) left, (int) top, (int) left + child.getMeasuredWidth(), (int) top + child.getMeasuredHeight());
}
}
/**
* 添加子控件
*/
private void addImgIcon() {
for (int i = 1; i < mImageList.size() + 1; i++) {
//新建imageview
final ImageView mImageView = new ImageView(getContext());
mImageView.setImageResource(mImageList.get(i - 1));
LayoutParams layoutParams = null;
mImageView.setScaleType(ImageView.ScaleType.FIT_XY);
if (is_right_select_icon){
//右侧icon为选中状态
if (i==mImageList.size()){
mImageView.setAlpha(1f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}else {
mImageView.setAlpha(0.5f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}
}else {
// 左侧icon为选中状态
if (i==5){
mImageView.setAlpha(1f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}else {
mImageView.setAlpha(0.5f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}
}
mImageView.setLayoutParams(layoutParams);
final int finalI = i;
//添加点击事件
mImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (isCanClick) {
//  Toast.makeText(getContext(),finalI + " ---", Toast.LENGTH_SHORT).show();
if (mOnLogoItemClick != null)
mOnLogoItemClick.onItemClick(view, finalI - 1);
}
}
});
//添加view
addView(mImageView);
}
//添加view点击事件
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (isCanClick) {
}
}
});
}
/**
* 触摸监听
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (mCanScrool) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
mDownTime = System.currentTimeMillis();
mTmpAngle = 0;
// 如果当前已经在快速滚动
if (isMove) {
// 移除快速滚动的回调
removeCallbacks(mScrollResetRunnable);
isMove = false;
return true;
}
break;
case MotionEvent.ACTION_MOVE:
/**
* 获得开始的角度
*/
float start = getAngle(mLastX, mLastY);
/**
* 获得当前的角度
*/
float end = getAngle(x, y);
Log.e("TAG", "start = " + start + " , end =" + end);
// 一四象限
if (getQuadrant(x, y) == 1 || getQuadrant(x, y) == 4) {
mStartAngle += end - start;
mTmpAngle += end - start;
//二三象限
} else {
mStartAngle += start - end;
mTmpAngle += start - end;
}
// 重新布局
getCheck();
break;
case MotionEvent.ACTION_UP:
// 获取每秒移动的角度
float anglePerSecond = mTmpAngle * 1000
/ (System.currentTimeMillis() - mDownTime);
// 如果达到最大速度
if (Math.abs(anglePerSecond)   mMax_Speed && !isMove) {
// 惯性滚动
post(mScrollResetRunnable = new ScrollResetRunnable(anglePerSecond));
return true;
}
// 如果当前旋转角度超过minSpeed屏蔽点击
if (Math.abs(mTmpAngle)   mMin_Speed) {
return true;
}
break;
}
}
return super.dispatchTouchEvent(event);
}
/**
* 获取移动的角度
*/
private float getAngle(float xTouch, float yTouch) {
double x = xTouch - (mRadius / 2d);
double y = yTouch - (mRadius / 2d);
return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
}
/**
* 根据当前位置计算象限
*/
private int getQuadrant(float x, float y) {
int tmpX = (int) (x - mRadius / 2);
int tmpY = (int) (y - mRadius / 2);
if (tmpX  = 0) {
return tmpY  = 0 ? 4 : 1;
} else {
return tmpY  = 0 ? 3 : 2;
}
}
/**
* 在activity的onCreate方法中获取当前自定义view中在屏幕中的绝对坐标始终为0,
* 改成在onWindowFocusChanged函数中获取即可,这时view都已经加载完成
* 但这里特别注意一点要:如果是fragment种使用该自定义view的话,这里的方法就应该注释掉
* 因为不但获取到的矩形的值是空的,而且当你的fragment执行了跳转的逻辑后,再返回后会发
* 一种特别恶心的异常,你获取到判断选中位置的矩形的left,top,right,bottom的值会和
* 初始化的时候不一样,导致你选中时候的状态出现异常情况,本人已经被坑过,希望后面的同学
* 一定注意吸取教训
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
getSelectIconReft();
}
//获取选中icon位置的矩形范围
private void getSelectIconReft() {
int [] location = new int [2];
getLocationOnScreen(location);
//计算出右侧选中时图标的位置
if (is_right_select_icon){
//选中的icon动态设置宽高为60,没选中宽高55,这里60/2为选中按钮的宽度或者高度的一半,即中心点
select_icon_rect.left = location[0]+getWidth()-mCircleLineStrokeWidth/2-DensityUtil.dip2px(getContext(),40f)/2;
select_icon_rect.top =(location[1]+getHeight()/2)-DensityUtil.dip2px(getContext(),40f)/2;
select_icon_rect.right = location[0]+getWidth()-mCircleLineStrokeWidth/2+DensityUtil.dip2px(getContext(),40f)/2;
select_icon_rect.bottom = (location[1]+getHeight()/2)+DensityUtil.dip2px(getContext(),40f)/2;
}else {
//计算出左侧选中时图标的位置
//选中的icon动态设置宽高为60,没选中宽高55,这里60/2为选中按钮的宽度或者高度的一半,即中心点
select_icon_rect.left = location[0]+mCircleLineStrokeWidth/2-DensityUtil.dip2px(getContext(),40f)/2;
select_icon_rect.top = (location[1]+getHeight()/2)-DensityUtil.dip2px(getContext(),40f)/2;
select_icon_rect.right = location[0]+mCircleLineStrokeWidth/2+DensityUtil.dip2px(getContext(),40f)/2;
select_icon_rect.bottom = (location[1]+getHeight()/2)+DensityUtil.dip2px(getContext(),40f)/2;
}
Log.d("onFocusChanged","-----getHeight=="+getChildAt(0).getHeight()+";getWidth=="+getChildAt(0).getWidth());
}
/**
* 通过角度判断象限
*/
private int getQuadrantByAngle(int angle) {
if (angle <= 90) {
return 4;
} else if (angle <= 180) {
return 3;
} else if (angle <= 270) {
return 2;
} else {
return 1;
}
}
/**
* 惯性滚动
*/
private class ScrollResetRunnable implements Runnable {
private float angelPerSecond;
public ScrollResetRunnable(float velocity) {
this.angelPerSecond = velocity;
}
public void run() {
//小于20停止
if ((int) Math.abs(angelPerSecond) < 20) {
isMove = false;
return;
}
isMove = true;
// 滚动时候不断修改滚动角度大小
// mStartAngle += (angelPerSecond / 30);
mStartAngle += (angelPerSecond / 40);
//逐渐减小这个值
angelPerSecond /= 1.0666F;
postDelayed(this, 30);
// 重新布局
getCheck();
}
}
/**
* 点击事件接口
*/
public interface OnLogoItemClick {
void onItemClick(View view, int pos);
}
private OnLogoItemClick mOnLogoItemClick;
private OnIconSelectedListener mOnIconSelectedListener;
/**
* 设置点击事件
* @param mOnLogoItemClick
*/
public void addOnItemClick(OnLogoItemClick mOnLogoItemClick) {
this.mOnLogoItemClick = mOnLogoItemClick;
}
/**
* 到选中位置后选中事件接口
*/
public interface OnIconSelectedListener{
void onIconSelected( int pos);
}
/**
* 设置点击事件
* @param mOnIconSelectedListener
*/
public void addOnIconSelectedListener(OnIconSelectedListener mOnIconSelectedListener) {
this.mOnIconSelectedListener = mOnIconSelectedListener;
}
/**
* 旋转圆盘
*/
private void getCheck() {
mStartAngle %= 360;
setRotation(mStartAngle);
//改变选中的icon的状态
setSelectedIcon();
}
//改变选中的icon的状态
private void setSelectedIcon() {
if (select_icon_rect.left==0&&select_icon_rect.top==0){
//fragment中onWindowFocusChanged会出现计算select_icon_rect.left和select_icon_rect.top等于0的情况,
// 所以做下判断,如果为0则重新调用下计算方法
getSelectIconReft();
}
for (int j =0;j<getChildCount();j++){
LayoutParams layoutParams = null;
int [] location = new int [2];
getChildAt(j).getLocationOnScreen(location);
Log.d("getCheck","location[0]=="+location[0]+";select_icon_rect.left=="+select_icon_rect.left+"location[1]=="+location[1]+";select_icon_rect.top=="+select_icon_rect.top);
if (is_right_select_icon){
//右边icon是选中状态的时候
if (select_icon_rect.left-22<=location[0]&&location[0]<=select_icon_rect.right+22){
if (select_icon_rect.top-22<=location[1]&&location[1]<=select_icon_rect.bottom+22){
getChildAt(j).setAlpha(1);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
//把选中的icon所在list中的position通过接口回传过去
if (mOnIconSelectedListener!=null){
mOnIconSelectedListener.onIconSelected(j);
}
}else {
getChildAt(j).setAlpha(0.5f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}
}else {
getChildAt(j).setAlpha(0.5f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}
}else {
//左边icon是选中状态的时候
if (select_icon_rect.left-22<=location[0]&&location[0]<=select_icon_rect.right+22){
if (select_icon_rect.top-22<=location[1]&&location[1]<=select_icon_rect.bottom+22){
getChildAt(j).setAlpha(1);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
//把选中的icon所在list中的position通过接口回传过去
if (mOnIconSelectedListener!=null){
mOnIconSelectedListener.onIconSelected(j);
}
}else {
getChildAt(j).setAlpha(0.5f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}
}else {
getChildAt(j).setAlpha(0.5f);
layoutParams = new LayoutParams(DensityUtil.dip2px(getContext(),40f), DensityUtil.dip2px(getContext(),40f));
}
}
getChildAt(j).setLayoutParams(layoutParams);
getChildAt(j).invalidate();
Log.d("getChildCount","=="+j+";class=="+getChildAt(j).getClass()+";left=="+getChildAt(j).getLeft()+";top=="+getChildAt(j).getTop()+";right=="+getChildAt(j).getRight()+";bottom=="+getChildAt(j).getBottom()+";getLocationOnScreen:x="+location[0]+"y="+location[1]+";getRotationX=="+getRotationX()+";getRotationY=="+getRotationY());
}
}
}

然后就是你在activity中根据回调方法获取选中的对象: //左右侧方法相同,这里列出左侧圆盘获取方法:

view.ringView_half_left.addOnIconSelectedListener { position - 
// ToDo 根据postion从你的list中获取对应的选中的对象的bean类属性即可
}

最后贴下布局文件:

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" 
<RelativeLayout
android:id="@+id/ring_left_outside_rl"
android:layout_width="@dimen/dp_350"
android:layout_height="@dimen/dp_350"
android:layout_alignParentLeft="true"
android:layout_marginLeft="-240dp" 
<com.wj.views.RingViewHalf
android:id="@+id/ringView_half_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_compatibility"
app:image_angle_rh="15"
app:image_padding_rh="20"
app:is_right_select_icon_rh="true"
app:list_rh="@array/zodiac_list" / 
<ImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/dp_220"
android:src="@drawable/icon_match_boy" / 
</RelativeLayout 
<RelativeLayout
android:id="@+id/ring_right_outside_rl"
android:layout_width="@dimen/dp_350"
android:layout_height="@dimen/dp_350"
android:layout_alignParentRight="true"
android:layout_marginRight="-240dp" 
<com.wj.views.RingViewHalf
android:id="@+id/ringView_half_right"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/bg_compatibility"
app:image_angle_rh="15"
app:image_padding_rh="20"
app:is_right_select_icon_rh="false"
app:list_rh="@array/zodiac_list" / 
<ImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/dp_220"
android:src="@drawable/icon_match_girl" / 
</RelativeLayout 
<Button
android:id="@+id/check_btn"
android:layout_width="@dimen/dp_265"
android:layout_height="@dimen/dp_46"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/dp_25"
android:layout_marginTop="@dimen/dp_25"
android:layout_centerHorizontal="true"
android:alpha="0.5"
android:enabled="true"
android:clickable="true"
android:background="@drawable/check" / 
</RelativeLayout  

//这里是放半圆形转盘选择器上显示的图片list,我这里是用的xml静态传进去的,也可以改为动态方式传递

app:list_rh="@array/zodiac_list" 

然后在values下面创建一个arrays.xml文件

<?xml version="1.0" encoding="utf-8"? 
<resources 
<string-array name="zodiac_list" 
<item @drawable/zodiac_1</item 
<item @drawable/zodiac_2</item 
<item @drawable/zodiac_3</item 
<item @drawable/zodiac_4</item 
<item @drawable/zodiac_5</item 
<item @drawable/zodiac_6</item 
<item @drawable/zodiac_7</item 
<item @drawable/zodiac_8</item 
<item @drawable/zodiac_9</item 
<item @drawable/zodiac_10</item 
<item @drawable/zodiac_11</item 
<item @drawable/zodiac_12</item 
</string-array 
</resources 

到此就可以了。

以上就是本文的全部内容,希望对大家的学习有所帮助。

更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》

1.Android车载应用开发系统学习指南(附项目实战)

2.Android Framework学习指南,助力成为系统级开发高手

3.2024最新Android中高级面试题汇总+解析,告别零offer

4.企业级Android音视频开发学习路线+项目实战(附源码)

5.Android Jetpack从入门到精通,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高级Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360°全方面性能调优

10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/501885.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

手机销量分析案例

项目背景 某电商商城随着业务量的发展&#xff0c;积累了大量的用户手机销售订单数据。决策层希望能够通过对这些数据的分析了解更多的用户信息及用户的分布&#xff0c;从而可以指导下一年的市场营销方案以及更加精准的定位市场&#xff0c;进行广告投放。 数据说明 数据时…

链表基础题

206. 反转链表 问题描述 给定单链表的头节点 head &#xff0c;请反转链表&#xff0c;并返回反转后的链表的头节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;…

CXL事务层(续)

3.2 CXL.cache 3.2.1 概览 CXL.cache协议将设备和主机之间的交互定义为多个请求&#xff0c;每个请求至少有一条相关的响应消息&#xff0c;有时还有数据传输。该接口在每个方向上由三个通道组成&#xff1a;请求&#xff08;Request&#xff09;、响应&#xff08;Response&…

Docker Desktop 在 Windows 上的安装和使用

目录 1、安装 Docker Desktop 2、使用 Docker Desktop &#xff08;1&#xff09;运行容器 &#xff08;2&#xff09;查看容器信息 &#xff08;3&#xff09;数据挂载 Docker Desktop是Docker的官方桌面版&#xff0c;专为Mac和Windows用户设计&#xff0c;提供了一个简…

记录rocketMQ5.+启动报错解决过程

1.根据官方文档指引下载对应的rocketMQ源码包&#xff0c;上传到服务器解压 2. 启动NameServer nohup sh bin/mqnamesrv & 验证namesrv是否启动成功 tail -f ~/logs/rocketmqlogs/namesrv.log The Name Server boot success… 3.启动BrokerProxy nohup sh bin/mqbroker -n …

HuTool工具箱验证JWT生成Token失败

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于…

羡青山有思,Java有接口

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

网络安全-内网DNS劫持-ettercap

前言 一&#xff0c;我也是初学者记录的笔记 二&#xff0c;可能有错误的地方&#xff0c;请谨慎 三&#xff0c;欢迎各路大神指教 四&#xff0c;任何文章仅作为学习使用 五&#xff0c;学习网络安全知识请勿适用于违法行为 学习网络安全知识请勿适用于违法行为 学习网络安全…

XR虚拟直播间,引领创新风潮,打破直播局限!

随着互联网技术日新月异的发展&#xff0c;直播行业也迎来了蓬勃发展的春天。然而&#xff0c;大多数直播间在吸引观众眼球和延长用户观看时长方面&#xff0c;仍然面临着巨大的挑战。正是在这样的背景下&#xff0c;XR虚拟直播系统应运而生&#xff0c;以其多维度的直播场景、…

香港服务器怎么看是CN2 GT线路还是CN2 GIA线路?

不知道有没有小伙伴们注意过&#xff0c;很多人在租用香港服务器的时候都习惯性选择 CN2 线路&#xff1f;仿佛香港服务器是否采用 CN2 线路成为个人企业选择香港服务器的一个标准。其实&#xff0c;香港服务器有CN2、优化直连(163)、BGP多线(包含了国际和国内线路)&#xff0c…

unity小:使用Unity FBX Exporter 将 3DMax场景或者模型无损导入Unity

本指南旨在帮助您顺利安装和配置Unity FBX Exporter插件&#xff0c;并解决相关的常见问题。 安装 FBX Exporter 下载并安装FBX Exporter插件。 打开Unity&#xff0c;选择 Edit > Project Settings > Fbx Export。 点击 Install Unity Integration 并选择3ds Max的插…

图片标注编辑平台搭建系列教程(4)——fabric几何定制渲染

背景 标注的几何&#xff0c;有时需要一些定制化的渲染样式&#xff0c;例如&#xff0c;线中间展示箭头&#xff0c;表示方向。本期教程教大家如何实现fabric几何定制化渲染。 带箭头的线 fabric提供了一些原生的几何&#xff0c;例如Point、Polyline、Polygon。同时提供了…

编程器固件修改教程

首发csdn&#xff0c;转载请说明出处&#xff0c;保留一切权益。 关于编程器固件 所谓编程器固件是用编程器读取嵌入式设备的FLASH存储数据生成的文件&#xff0c;类似于直接用工具复制整个硬盘 编程器固件与普通固件的差异 编程器固件是用特定的结构(按顺序、大小)将一些文件系…

私有库 nexus Dependency ‘SNAPSHOT‘ not found

私有库 nexus 提示 Dependency SNAPSHOT not found jar包无法下载&#xff0c;困扰了很久&#xff0c;后来才发现&#xff0c;是因为只在 dependencyManagement 里写了引用&#xff0c;没有在具体的 dependencies 里引用&#xff0c;导致无法获取到jar包&#xff0c;低级错误…

测试用例设计方法:正交试验法详解!

01、正交试验法介绍 正交试验法是研究多因素、多水平的一种试验法&#xff0c;它是利用正交表来对试验进行设计&#xff0c;通过少数的试验替代全面试验&#xff0c;根据正交表的正交性从全面试验中挑选适量的、有代表性的点进行试验&#xff0c;这些有代表性的点具备了“均匀…

Linux学习教程 Linux入门教程(超全面 超详细)收藏这一篇就够了

Linux是什么&#xff1f; linux是一个开源、免费的操作系统&#xff0c;其稳定性、安全性、处理多并发能力已经得到业界的认可&#xff0c;目前大多数企业级应用甚至是集群项目都部署运行在linux操作系统之上&#xff0c;很多软件公司考虑到开发成本都首选linux&#xff0c;在…

文本批量高效处理,自动粘贴筛选网址,轻松管理海量链接

在互联网时代&#xff0c;网址链接成为我们获取信息、沟通交流的重要桥梁。然而&#xff0c;面对海量的网址链接&#xff0c;如何快速筛选、整理并管理它们成为了一个挑战。为了解决这一问题&#xff0c;我们特别推出了智能网址筛选功能&#xff0c;让您在自动粘贴的同时&#…

软考103-上午题-【结构化开发】-模块独立

一、命题特点 考试类型&#xff1a; 选择题&#xff08;3~9分&#xff09;综合分系题&#xff08;数据流图【试题一】&#xff0c;15分&#xff09; 二、系统设计的基本原理 1、抽象 2、模块化 将一个待开发的软件分解为若干个小的简单部分——模块。 每个模块可以独立的开…

基于java+SpringBoot+Vue的校友社交系统设计与实现

基于javaSpringBootVue的校友社交系统设计与实现 开发语言: Java 数据库: MySQL技术: SpringBoot MyBatis工具: IDEA/Eclipse、Navicat、Maven 系统展示 前台展示 后台展示 系统简介 整体功能包含&#xff1a; 校友社交系统是一个为校友提供一个交流互动、信息共享的平台…

ObjectiveC-03-XCode的使用和基础数据类型

本节做为Objective-C的入门课程&#xff0c;笔者会从零基础开始介绍这种程序设计语言的各个方面。 术语 ObjeC&#xff1a;Objective-C的简称&#xff0c;因为完整的名称过长&#xff0c;后续会经缩写来代替&#xff1b;项目/工程&#xff1a;也称工程&#xff0c;指的是一个A…