首页 文章 精选 留言 我的

精选列表

搜索[人设自定义],共10000篇文章
优秀的个人博客,低调大师

spring boot redis 自定义序列化方式(fastJson)

@Configuration public class RedisConfig { @Bean public RedisSerializer fastJsonJsonRedisSerializer() { return new FastJsonRedisSerializer(Object.class); } @Bean public RedisTemplate initRedisTemplate(RedisConnectionFactory redisConnectionFactory, RedisSerializer fastJsonJsonRedisSerializer) throws Exception { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setValueSerializer(fastJsonJsonRedisSerializer); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } }

优秀的个人博客,低调大师

Android 自定义优雅的BezierSeekBar 之撸码解析

0x0 前言 某设计网经常会有很多优秀漂亮的交互设计作品,有一天,偶遇这样的效果,动画流畅,交互自然,于是埋头自己解剖其中的元素,做了个开源控件,十来天有了一百来个star,觉得很受欢迎,今天专门写这潦草几笔,分享案发经过,希望对同行有所帮助。 0x1 准备 效果图 preview 效果分析 曲线部分 两个三阶贝塞尔曲线 1、默认状态:直线,首尾标注,默认值标注。 2、手指按下:曲线动画执行,标识小圆规则地放大,值的标注根据曲线波峰相对位置不变,向上同速移动,同时,标注背景渐变加深,动画结束。 3、手指拖动效果:曲线、小圆形、标注三者同时跟随触摸点移动,同时更新标注值。 4、手指离开屏幕:曲线收回动画执行,标识小圆规则缩小到默认状态,选中的值跟随波峰下沉到默认状态,标注背景渐变消失,动画结束。 圆形指示器部分 缩放效果 拆解状态三部分:默认状态、触摸过程中、触摸后状态。其中默认状态下指示器很小的圆形,距离水平曲线下方一个约定距离,当按下过程中,圆最下方坐标不变,圆直径逐渐增大,圆顶部与曲线的距离不变,直到动画结束。 功能分析 1、控件内元素:标尺、标注用的小圆、选中值,均可配置各自的颜色, 2、可配置值范围, 3、可配置默认值, 4、可实时监听选中的值。 5、可显示单位。 技术分析 曲线部分 通过静态截图可知本控件主要元素为触摸触发的曲线和其伸缩效果。让我们来简单分析一下曲线部分的结构: //这里展示曲线拆解图 拆解后,触摸部分为六阶贝塞尔曲线,五个基准点,四个控制点,我们将它拆分成两个三阶曲线即可。其中,首尾基准点的Y坐标固定,X坐标随着触摸位置相对移动,剩下的基准点X坐标相对固定,Y坐标根据动画规律升降。再说控制点,为保障默认状态下,曲线部分为水平,首尾两个控制点的Y坐标固定,X坐标相对固定,中间两个控制点Y坐标和中间那个基准点一致,X相对中间基准点固定。 (通过上面的拆解,可以让曲线在默认状态下是一条水平直线,并且在按下状态下,与水平位置、波峰位置,能有比较自然的过度弧形,不至于那么生硬。) 圆形指示器部分 普通圆形,相对自身底部向上变大、向下缩小还原。 动画部分 按下时的动画采用普通的ValueAnimator,匀速LinearInterpolator。另外还有个选中值的背景变化,根据动画进度改变画笔Alpha值即可。 //这里写点动画代码啰。 0x2 代码实现 泡杯茶,挽起袖子开撸! 继承View: public class BezierSeekBar extends View { public BezierSeekBar(Context context) { super(context); init(context, null); } public BezierSeekBar(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } public BezierSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public BezierSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context, attrs); } private void init(Context context, AttributeSet attrs) { this.context = context; } } 先绘制出曲线效果: //第一个三阶曲线 bezierPath.moveTo(this.fingerX - circleRadiusMax * 2 * 3, (float) 2 * height / 3); bezierPath.cubicTo(this.fingerX - circleRadiusMax * 2 * 2, (float) 2 * height / 3, this.fingerX - circleRadiusMax * 2 * 1, (float) 2 * height / 3 - bezierHeight, this.fingerX, (float) 2 * height / 3 - bezierHeight); //第二个三阶曲线 bezierPath.moveTo(this.fingerX, (float) 2 * height / 3 - bezierHeight); bezierPath.cubicTo(this.fingerX + circleRadiusMax * 2, (float) 2 * height / 3 - bezierHeight, this.fingerX + circleRadiusMax * 2 * 2, (float) 2 * height / 3, this.fingerX + circleRadiusMax * 2 * 3, (float) 2 * height / 3); 改变其Y坐标,让曲线恢复默认状态: 绘制完整线条: //line1 bezierPath.reset(); bezierPath.moveTo(0, (float) 2 * height / 3); bezierPath.lineTo(this.fingerX - circleRadiusMax * 2 * 3, (float) 2 * height / 3); //bezier1 bezierPath.moveTo(this.fingerX - circleRadiusMax * 2 * 3, (float) 2 * height / 3); bezierPath.cubicTo(this.fingerX - circleRadiusMax * 2 * 2, (float) 2 * height / 3, this.fingerX - circleRadiusMax * 2 * 1, (float) 2 * height / 3 - bezierHeight, this.fingerX, (float) 2 * height / 3 - bezierHeight); //bezier2 bezierPath.moveTo(this.fingerX, (float) 2 * height / 3 - bezierHeight); bezierPath.cubicTo(this.fingerX + circleRadiusMax * 2, (float) 2 * height / 3 - bezierHeight, this.fingerX + circleRadiusMax * 2 * 2, (float) 2 * height / 3, this.fingerX + circleRadiusMax * 2 * 3, (float) 2 * height / 3); //line2 bezierPath.lineTo(width, (float) 2 * height / 3); canvas.drawPath(bezierPath, bezierPaint); 添加Touch事件拦截,按下时显示曲线: @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: fingerX = event.getX(); if (fingerX < 0F) fingerX = 0F; if (fingerX > width) fingerX = width; //触摸介质进入控件,开始动画过渡 this.animatorFingerIn.start(); break; case MotionEvent.ACTION_MOVE: fingerX = event.getX(); if (fingerX < 0F) fingerX = 0F; if (fingerX > width) fingerX = width; postInvalidate(); break; case MotionEvent.ACTION_UP: //触摸介质离开控件,执行动画 this.animatorFingerOut.start(); break; } valueSelected = Integer.valueOf(decimalFormat.format(valueMin + (valueMax - valueMin) * fingerX / width)); if (selectedListener != null) { selectedListener.onSelected(valueSelected); } return true; } 添加动画效果: this.animatorFingerIn = ValueAnimator.ofFloat(0f, 1f); this.animatorFingerIn.setDuration(200L); this.animatorFingerIn.setInterpolator(new LinearInterpolator()); this.animatorFingerOut = ValueAnimator.ofFloat(1f, 0f); this.animatorFingerOut.setDuration(200L); this.animatorFingerOut.setInterpolator(new LinearInterpolator()); this.animatorFingerOut.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float progress = (float) animation.getAnimatedValue(); animInFinshed = (progress >= 0.15F); txtSelectedBgPaint.setAlpha((int) (255 * (progress - 0.15F))); if (progress >= 0.95F) { textPaint.setColor(colorValueSelected); } else { textPaint.setColor(colorValue); } bezierHeight = circleRadiusMax * 1.5F * progress; circleRadius = circleRadiusMin + (circleRadiusMax - circleRadiusMin) * progress; spaceToLine = circleRadiusMin * 2 * (1F - progress); postInvalidate(); } }); 绘制圆形指示器,根据动画进度改变其大小: canvas.drawCircle(this.fingerX, (float) 2 * height / 3 + spaceToLine + circleRadius, circleRadius, ballPaint); 添加其它辅助元素后,配置通用属性,抛出公共方法: <declare-styleable name="BezierSeekBar"> //曲线颜色 <attr name="bsBar_color_line" format="reference|color" /> //圆形指示器颜色 <attr name="bsBar_color_ball" format="reference|color" /> //阀值的文本颜色 <attr name="bsBar_color_value" format="reference|color" /> //选中值的文本颜色 <attr name="bsBar_color_value_selected" format="reference|color" /> //选中值的文本颜色背景 <attr name="bsBar_color_bg_selected" format="reference|color" /> //阀值最小 <attr name="bsBar_value_min" format="integer" /> //阀值最大 <attr name="bsBar_value_max" format="integer" /> //默认选中值 <attr name="bsBar_value_selected" format="integer" /> //单位 <attr name="bsBar_unit" format="reference|string" /> </declare-styleable> private void initAttr(Context context, AttributeSet attrs) { if (attrs != null) { TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.BezierSeekBar); this.colorBall = attributes.getColor(R.styleable.BezierSeekBar_bsBar_color_ball, Color.BLACK); this.colorLine = attributes.getColor(R.styleable.BezierSeekBar_bsBar_color_line, Color.BLACK); this.colorValue = attributes.getColor(R.styleable.BezierSeekBar_bsBar_color_value, Color.BLACK); this.colorValueSelected = attributes.getColor(R.styleable.BezierSeekBar_bsBar_color_value_selected, Color.WHITE); this.colorBgSelected = attributes.getColor(R.styleable.BezierSeekBar_bsBar_color_bg_selected, Color.BLACK); this.valueMin = attributes.getInteger(R.styleable.BezierSeekBar_bsBar_value_min, 30); this.valueMax = attributes.getInteger(R.styleable.BezierSeekBar_bsBar_value_max, 150); this.valueSelected = attributes.getInteger(R.styleable.BezierSeekBar_bsBar_value_selected, 65); this.unit = attributes.getString(R.styleable.BezierSeekBar_bsBar_unit) + ""; attributes.recycle(); } } 最后,测试一下: <tech.nicesky.bezierseekbar.BezierSeekBar android:id="@+id/bsBar_test" app:bsBar_color_ball="@android:color/white" app:bsBar_color_bg_selected="@android:color/white" app:bsBar_color_line="@android:color/white" app:bsBar_color_value="@android:color/white" app:bsBar_color_value_selected="#ef5350" app:bsBar_value_min="30" app:bsBar_value_max="120" app:bsBar_value_selected="65" app:bsBar_unit="kg" android:layout_width="match_parent" android:layout_height="wrap_content" /> 完美 :) ! 原文发布时间为:2018-11-19 本文作者:fairytale110 本文来自云栖社区合作伙伴“ 安卓巴士Android开发者门户”,了解相关信息可以关注“ 安卓巴士Android开发者门户”。

优秀的个人博客,低调大师

04.自定义View(SlidingView仿QQ侧滑)

感谢红橙Darren博主 布局文件中 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context="com.app.rzm.test.TestSlidingViewActivity"> <com.rzm.commonlibrary.views.SlidingView app:menuPaddingRight="100dp" app:menuParallax="true" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:background="#ffffff" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:text="我在侧边位置" android:textSize="50sp" android:layout_height="match_parent" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:background="@color/transparent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:text="我在主版位置" android:textSize="50sp" android:layout_height="match_parent" /> </LinearLayout> </LinearLayout> </com.rzm.commonlibrary.views.SlidingView> </LinearLayout> package com.rzm.commonlibrary.views; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; import android.widget.ImageView; import com.rzm.commonlibrary.R; import com.rzm.commonlibrary.utils.LogUtils; /** * Created by renzhenming on 2018/3/28. * * SlidingView继承自HorizontalScrollView,所以在布局中使用的时候,需要设置一个唯一的子View,然后 * 往这个子 view中添加侧边栏和主栏 */ public class SlidingView extends HorizontalScrollView{ private final String TAG = getClass().getSimpleName(); //侧滑菜单的宽度 private final int mMenuWidth; private final Context mContext; //默认的侧边栏滑出最大位置距离右边屏幕的距离 private float mMenuPaddingRight = 100; //侧边菜单布局 private View mMenuView; //主页内容布局,包括用户的contentView和我们添加的阴影效果 private ViewGroup mContentView; //侧边栏menu是否打开 private boolean mMenuOpened; //设置抽屉模式 private boolean mParallaxMode; //手势监听器 private GestureDetector mGestureDetector; //快速滑动打开收起侧边栏的敏感指数,越大敏感度越小,约不容易打开关闭 //不能过大也能过小, private float mSensitivity = 1000; //设置阴影 private ImageView mShadowIv; //视差模式下,默认的侧边栏缩进的距离相对于侧边栏宽度的比例 private float mTransitionPercent = 0.8f; //阴影透明度能达到的最深颜色值 private String mAlphaColor="#99000000"; public SlidingView(Context context) { this(context,null); } public SlidingView(Context context, AttributeSet attrs) { this(context, attrs,-1); } public SlidingView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SlidingView); mMenuPaddingRight = typedArray.getDimension(R.styleable.SlidingView_menuPaddingRight, dp2px((int) mMenuPaddingRight)); mParallaxMode = typedArray.getBoolean(R.styleable.SlidingView_menuParallax, mParallaxMode); //侧边栏的宽度 mMenuWidth = (int) (getScreenWidth() - mMenuPaddingRight); typedArray.recycle(); mGestureDetector = new GestureDetector(context, new GestureListener()); } @Override public boolean onTouchEvent(MotionEvent ev) { //如果需要GestureDetector处理,则由其处理 if (mGestureDetector.onTouchEvent(ev)){ return mGestureDetector.onTouchEvent(ev); } switch (ev.getAction()){ case MotionEvent.ACTION_UP: //这个位置指的是,当前这个view和手机屏幕左边届交点距离view最左侧的距离,这里最小为0,不会为负 //当侧边栏滑出的过程中,scrollX呈递减的趋势,直到=0 int scrollX = getScrollX(); LogUtils.d(TAG,"scrollX:"+scrollX); if (scrollX > mMenuWidth/2){ //滑出了一部分,但是还没有到宽度的1/2 closeMenu(); }else{ openMenu(); } return false; } return super.onTouchEvent(ev); } /** * 打开侧边栏 */ private void openMenu() { smoothScrollTo(0,0); mMenuOpened = true; } /** * 关闭侧边栏 */ private void closeMenu() { smoothScrollTo(mMenuWidth,0); mMenuOpened = false; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); //默认设置侧滑菜单隐藏 if (changed){ //scrollTo 让当前屏幕左边界滑动到(mMenuWidth,0)这个坐标位置 scrollTo(mMenuWidth, 0); } } @Override protected void onFinishInflate() { super.onFinishInflate(); //获取根view ViewGroup rootView = (ViewGroup) getChildAt(0); int childCount = rootView.getChildCount(); if (childCount > 2){ throw new IllegalStateException("you should add two child view at most"); } //获取两个布局 mMenuView = rootView.getChildAt(0); //给内容添加阴影效果 mContentView = new FrameLayout(mContext); ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT); mContentView.setLayoutParams(contentParams); //获取原来的内容布局,并把原来的内容布局从LinearLayout中异常 View oldContentView = rootView.getChildAt(1); rootView.removeView(oldContentView); //把原来的内容View 和 阴影加到我们新创建的内容布局中 mContentView.addView(oldContentView); //创建阴影 mShadowIv = new ImageView(mContext); mShadowIv.setBackgroundColor(Color.parseColor(mAlphaColor)); mContentView.addView(mShadowIv); //把包含阴影的新的内容View 添加到 LinearLayout中 rootView.addView(mContentView); //设置二者宽度 mMenuView.getLayoutParams().width = mMenuWidth; mContentView.getLayoutParams().width = getScreenWidth(); } private float dp2px(int dp){ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,getResources().getDisplayMetrics()); } /** * 屏幕宽度 * @return */ public int getScreenWidth(){ Resources resources = getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); return metrics.widthPixels; } private class GestureListener extends GestureDetector.SimpleOnGestureListener{ // 处理手势快速滑动 @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //velocityX,velocityY都是朝坐标系正方向滑动为正,负方向为负,表示正方向上单位时间的速度 //velocityX为正,表示单位时间内, LogUtils.d(TAG,"velocityX:"+velocityX+",velocityY:"+velocityY); if (mMenuOpened){ if (velocityX < -mSensitivity){ toggleMenu(); return true; } }else{ if (velocityX>mSensitivity){ toggleMenu(); return true; } } return super.onFling(e1, e2, velocityX, velocityY); } } /** * 切换菜单的状态 */ private void toggleMenu() { if(mMenuOpened){ closeMenu(); LogUtils.d(TAG,"close"); }else{ openMenu(); LogUtils.d(TAG,"open"); } } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); // l 是 当前滚动的x距离 指的是当前view和屏幕左侧边界相交点距离view最左侧的距离 LogUtils.e(TAG,"onScrollChanged:l===="+l); if (mParallaxMode) { //在onLayout中,默认会将这个SlidingView向左移动一个侧边栏宽度 //这个时候onScrollChanged会被调用,这时候会执行这行代码,将侧边栏向x轴正方向移动一定比例的l宽度,实际上 //也此时,侧边栏已经被放在了主也contentView的下边,此时,滑动contentView的时候,会达到一种视觉差异效果 mMenuView.setTranslationX(l * mTransitionPercent); } //给内容添加阴影效果 - 计算梯度值 float gradientValue = l * 1f / mMenuWidth; LogUtils.e(TAG,"gradientValue:"+gradientValue); //这是 1 - 0 变化的值 //给内容添加阴影效果 - 给阴影的View指定透明度 0 - 1 变化的值 float shadowAlpha = 1 - gradientValue; LogUtils.e(TAG,"shadowAlpha:"+shadowAlpha); mShadowIv.setAlpha(shadowAlpha); } }

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册