首页
工具
心境语句
相册
建站轨迹
关于
Search
1
微信小程序:计算属性的两种体现方式及应用场景
1,652 阅读
2
Antd Upload 组件上传文件接收数据流并下载
1,172 阅读
3
unlock-music工具介绍
658 阅读
4
[C#]使用dnSpy对目标程序(EXE或DLL)进行反编译修改并编译运行
653 阅读
5
C#插件火车头采集器动态切换代理IP,及自动切换UserAgent
627 阅读
react
typecho
ASP
Centos
MYSQL
PHP
Sql server
Javascript
nodejs
数据采集
.NET
git
编程算法
管理及流程
Vue
微信小程序
android
python
mongodb
登录
Search
标签搜索
kotlin
node-sass
nuxtjs
C#火车头插件
火车头采集器
火车头代理
C#反编译
程序逆向
dnSpy教程
Antd
InputNumber
NPM教程
NPM命令
rrweb教程
git慢
git镜像
vim命令
git命令
网页音乐插件
网页播放器
Elysian
累计撰写
75
篇文章
累计收到
0
条评论
首页
栏目
react
typecho
ASP
Centos
MYSQL
PHP
Sql server
Javascript
nodejs
数据采集
.NET
git
编程算法
管理及流程
Vue
微信小程序
android
python
mongodb
页面
工具
心境语句
相册
建站轨迹
关于
搜索到
3
篇与
android
的结果
2022-05-18
安卓组件,仿IOS平台UISegmentControlView
该文章主要是做一个备忘记录转至:https://blog.csdn.net/yeah0126/article/details/51452508感谢作者控件简介UISegmentControl在IOS平台的App中非常常见,其控件如下图所示:IOS平台的UISegmentControl这种控件的主要作用是动态的更改界面的显示内容,一般应用于内容较多的界面,且分屏显示不同种类的内容。控件属性说明<declare-styleable name="SegmentControlView"> <attr name="scv_BackgroundSelectedColor" format="reference|color" /><!--选中segment的背景颜色--> <attr name="scv_BackgroundNormalColor" format="reference|color" /><!--未选中segment的背景颜色--> <attr name="scv_TextSelectedColor" format="reference|color" /><!--选中segment的文字颜色--> <attr name="scv_TextNormalColor" format="reference|color" /><!--未选中segment的文字颜色--> <attr name="scv_FrameColor" format="reference|color" /><!--segment边框的颜色--> <attr name="scv_FrameWidth" format="reference|dimension" /><!--segment边框的宽度--> <attr name="scv_FrameCornerRadius" format="reference|dimension" /><!--segment四个圆角的半径大小--> <attr name="scv_TextSize" format="reference|dimension" /><!--文字大小--> <attr name="scv_TextArray" format="reference" /><!--string数组,每一个string都会填充到一个segment中--> <attr name="scv_SelectedIndex" format="reference|integer" /><!--默认选中的segment--> <attr name="scv_SegmentPaddingHorizontal" format="reference|dimension" /><!--每一个segment内部的水平padding--> <attr name="scv_SegmentPaddingVertical" format="reference|dimension" /><!--每一个Segment的竖直方向的padding--> <attr name="scv_Gradient" format="reference|boolean" /><!--Segment改变时是否使用颜色渐变效果--> </declare-styleable>布局文件创建SegmentControlView<cn.carbs.android.segmentcontrolview.library.SegmentControlView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:paddingLeft="10dp" android:paddingRight="10dp" app:scv_FrameCornerRadius="6dp" app:scv_FrameWidth="1dp" app:scv_Gradient="true" app:scv_SegmentPaddingVertical="5dp" app:scv_TextArray="@array/segment_control_arrays_0"/>使用方法引用dependencies { implementation 'cn.carbs.android:SegmentControlView:1.0.0' }使用segmentcontrolview.setOnSegmentChangedListener(OnSegmentChangedListener { newSelectedIndex -> if (viewpager != null) { //change the second argument to true if you want the gradient effect when viewpager is changing viewpager.setCurrentItem(newSelectedIndex, false) //viewpager changing without animation } }) //set viewpager to change segment according to the state of viewpager segmentcontrolview.setViewPager(viewpager) //set the selected index of segments initiatively segmentcontrolview.setSelectedIndex() //set gradient effect if you want segmentcontrolview.setGradient(true)源码package cn.carbs.android.segmentcontrolview.library; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.support.v4.view.ViewPager; import android.text.TextUtils; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import java.lang.reflect.Field; /** * an SegmentControlView inspired by the UISegmentControl on IOS platform. * this view has many interesting such as : * 1. set gradient effect when segment is changing by ViewPager if needed * 2. set the corners' radius * 3. set colors of texts and background * 4. set titles in xml resource file * 5. has pressed effect when finger touches this view, you can set the dark coefficient * * Author Carbs.Wang * Email yeah0126#yeah.net */ public class SegmentControlView extends View { /** * onSegmentChanged function will be triggered if segment changed */ public interface OnSegmentChangedListener{ void onSegmentChanged(int newSelectedIndex); } private static final float TOUCHED_BACKGROUND_DARK_COEFFICIENT = 0.95F; private static final int COLOR_PRIMARY_NORMAL = 0XFFFFFFFF; private static final int COLOR_PRIMARY_SELECTED = 0XFF2CA99F; private static final int DEFAULT_COLOR_BACKGROUND_SELECTED = COLOR_PRIMARY_SELECTED; private static final int DEFAULT_COLOR_BACKGROUND_NORMAL = COLOR_PRIMARY_NORMAL; private static final int DEFAULT_COLOR_TEXT_SELECTED = COLOR_PRIMARY_NORMAL; private static final int DEFAULT_COLOR_TEXT_NORMAL = COLOR_PRIMARY_SELECTED; private static final int DEFAULT_COLOR_FRAME = COLOR_PRIMARY_SELECTED; private static final int DEFAULT_TEXT_SIZE_SP = 16; private static final int DEFAULT_FRAME_WIDTH_PX = 2; private static final int DEFAULT_FRAME_CORNER_RADIUS_PX = 0; private static final int DEFAULT_SELECTED_INDEX = 0; private static final int DEFAULT_SEGMENT_PADDING_HORIZONTAL = 16; private static final int DEFAULT_SEGMENT_PADDING_VERTICAL = 12; private static final boolean DEFAULT_IS_GRADIENT = false; private String[] mTexts = null; private int mColorBackgroundSelected = DEFAULT_COLOR_BACKGROUND_SELECTED; private int mColorBackgroundNormal = DEFAULT_COLOR_BACKGROUND_NORMAL; private int mColorTextSelected = DEFAULT_COLOR_TEXT_SELECTED; private int mColorTextNormal = DEFAULT_COLOR_TEXT_NORMAL; private int mColorFrame = DEFAULT_COLOR_FRAME; private int mFrameWidth = DEFAULT_FRAME_WIDTH_PX; private int mFrameCornerRadius = DEFAULT_FRAME_CORNER_RADIUS_PX; private int mTextSize = 0; private int mSelectedIndex = DEFAULT_SELECTED_INDEX; //used in wrap_content mode private int mSegmentPaddingHorizontal = DEFAULT_SEGMENT_PADDING_HORIZONTAL; private int mSegmentPaddingVertical = DEFAULT_SEGMENT_PADDING_VERTICAL; private boolean mIsGradient = DEFAULT_IS_GRADIENT; private OnSegmentChangedListener mOnSegmentChangedListener; private float unitWidth = 0; private Paint paintText; //painter of the text private Paint paintBackground; //painter of the background private Paint paintFrame; //painter of the frame private RectF rectF; private RectF rectFArc; private Path pathFrame; private float textCenterYOffset; private int preTouchedIndex = -1; private int curTouchedIndex = -1; private ViewPager viewPager; public SegmentControlView(Context context) { super(context); init(); } public SegmentControlView(Context context, AttributeSet attrs) { super(context, attrs); initAttr(context, attrs); init(); } public SegmentControlView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs); init(); } private void initAttr(Context context, AttributeSet attrs) { if (attrs == null) { return; } TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SegmentControlView); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); if(attr == R.styleable.SegmentControlView_scv_BackgroundSelectedColor){ mColorBackgroundSelected = a.getColor(attr, DEFAULT_COLOR_BACKGROUND_SELECTED); }else if(attr == R.styleable.SegmentControlView_scv_BackgroundNormalColor){ mColorBackgroundNormal = a.getColor(attr, DEFAULT_COLOR_BACKGROUND_NORMAL); }else if(attr == R.styleable.SegmentControlView_scv_TextSelectedColor){ mColorTextSelected = a.getColor(attr, DEFAULT_COLOR_TEXT_SELECTED); }else if(attr == R.styleable.SegmentControlView_scv_TextNormalColor){ mColorTextNormal = a.getColor(attr, DEFAULT_COLOR_TEXT_NORMAL); }else if(attr == R.styleable.SegmentControlView_scv_FrameColor){ mColorFrame = a.getColor(attr, DEFAULT_COLOR_FRAME); }else if(attr == R.styleable.SegmentControlView_scv_TextSize){ mTextSize = a.getDimensionPixelSize(attr, sp2px(getContext(), DEFAULT_TEXT_SIZE_SP)); }else if(attr == R.styleable.SegmentControlView_scv_TextArray){ mTexts = convertCharSequenceToString(a.getTextArray(attr)); }else if(attr == R.styleable.SegmentControlView_scv_FrameWidth){ mFrameWidth = a.getDimensionPixelSize(attr, DEFAULT_FRAME_WIDTH_PX); }else if(attr == R.styleable.SegmentControlView_scv_FrameCornerRadius){ mFrameCornerRadius = a.getDimensionPixelSize(attr, DEFAULT_FRAME_CORNER_RADIUS_PX); }else if(attr == R.styleable.SegmentControlView_scv_SelectedIndex){ mSelectedIndex = a.getInteger(attr, DEFAULT_SELECTED_INDEX); }else if(attr == R.styleable.SegmentControlView_scv_SegmentPaddingHorizontal){ mSegmentPaddingHorizontal = a.getDimensionPixelSize(attr, DEFAULT_SEGMENT_PADDING_HORIZONTAL); }else if(attr == R.styleable.SegmentControlView_scv_SegmentPaddingVertical){ mSegmentPaddingVertical = a.getDimensionPixelSize(attr, DEFAULT_SEGMENT_PADDING_VERTICAL); }else if(attr == R.styleable.SegmentControlView_scv_Gradient){ mIsGradient = a.getBoolean(attr, DEFAULT_IS_GRADIENT); } } a.recycle(); } private void init(){ rectF = new RectF(); rectFArc = new RectF(); pathFrame = new Path(); if(mTextSize == 0) mTextSize = sp2px(getContext(), DEFAULT_TEXT_SIZE_SP); paintText = new Paint(); paintText.setAntiAlias(true); paintText.setTextAlign(Paint.Align.CENTER); paintText.setTextSize(mTextSize); paintBackground = new Paint(); paintBackground.setAntiAlias(true); paintBackground.setStyle(Paint.Style.FILL); paintFrame = new Paint(); paintFrame.setAntiAlias(true); paintFrame.setStyle(Paint.Style.STROKE); paintFrame.setStrokeWidth(mFrameWidth); paintFrame.setColor(mColorFrame); textCenterYOffset = getTextCenterYOffset(paintText.getFontMetrics()); this.setClickable(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measureWidth(widthMeasureSpec, paintText), measureHeight(heightMeasureSpec, paintText)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); rectF.left = getPaddingLeft(); rectF.top = getPaddingTop(); rectF.right = w - getPaddingRight(); rectF.bottom = h - getPaddingBottom(); float inset = (float)Math.ceil(mFrameWidth / 2); rectF.inset(inset, inset); if(mTexts!= null && mTexts.length > 0){ unitWidth = rectF.width() / mTexts.length; } rectFArc.left = 0; rectFArc.top = 0; rectFArc.right = 2 * mFrameCornerRadius; rectFArc.bottom = 2 * mFrameCornerRadius; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(!isStringArrayEmpty(mTexts)){ drawBackgroundAndFrameAndText(canvas); } } @Override public boolean onTouchEvent(MotionEvent event) { preTouchedIndex = curTouchedIndex; switch(event.getAction()){ case MotionEvent.ACTION_DOWN: curTouchedIndex = getTouchedIndex(event.getX(), event.getY()); if(preTouchedIndex != curTouchedIndex){ invalidate(); } break; case MotionEvent.ACTION_MOVE: curTouchedIndex = getTouchedIndex(event.getX(), event.getY()); if(preTouchedIndex != curTouchedIndex){ invalidate(); } break; case MotionEvent.ACTION_UP: curTouchedIndex = getTouchedIndex(event.getX(), event.getY()); if(curTouchedIndex != -1){ if(mOnSegmentChangedListener != null && mSelectedIndex != curTouchedIndex){ mOnSegmentChangedListener.onSegmentChanged(curTouchedIndex); } mSelectedIndex = curTouchedIndex; } curTouchedIndex = -1; if(mIsGradient && checkViewPagerOnPageChangeListener(this.viewPager)){ }else{ invalidate(); } break; case MotionEvent.ACTION_CANCEL: curTouchedIndex = -1; invalidate(); break; } return super.onTouchEvent(event); } public void setTextSize(int textSize){ if(this.mTextSize != textSize){ this.mTextSize = textSize; paintText.setTextSize(textSize); textCenterYOffset = getTextCenterYOffset(paintText.getFontMetrics()); requestLayout(); invalidate(); } } public int getSelectedIndex(){ return mSelectedIndex; } public void setSelectedIndex(int selectedIndex){ if(mSelectedIndex != selectedIndex){ mSelectedIndex = selectedIndex; if(mOnSegmentChangedListener != null){ mOnSegmentChangedListener.onSegmentChanged(mSelectedIndex); } // invalidate(); if(mIsGradient && checkViewPagerOnPageChangeListener(this.viewPager)){ }else{ invalidate(); } } } public void setTextColor(int textColorNormal, int textColorSelected){ this.mColorTextNormal = textColorNormal; this.mColorTextSelected = textColorSelected; invalidate(); } public void setBackgroundColor(int backgroundColorNormal, int backgroundColorSelected){ this.mColorBackgroundNormal = backgroundColorNormal; this.mColorBackgroundSelected = backgroundColorSelected; invalidate(); } public void setFrameColor(int frameColor){ this.mColorFrame = frameColor; invalidate(); } public void setFrameWidth(int frameWidth){ this.mFrameWidth = frameWidth; requestLayout(); invalidate(); } public void setTexts(String[] texts){ assertTextsValid(texts); if(texts == null || texts.length < 2){ throw new IllegalArgumentException("SegmentControlView's content text array'length should larger than 1"); } if(checkIfEqual(this.mTexts, texts)){ return; } this.mTexts = texts; unitWidth = rectF.width() / texts.length; requestLayout(); invalidate(); } public int getCount(){ if(mTexts == null) return 0; return mTexts.length; } /** * setViewPager(viewpager) to response to the change of ViewPager * @param viewPager the viewPager you want segmentcontrolview to respond with */ public void setViewPager(ViewPager viewPager) { this.viewPager = viewPager; if (viewPager != null) { viewPager.setOnPageChangeListener(new InternalViewPagerListener()); } } /** * set if this view has the Gradient effect when segment changed * @param gradient set if you want gradient effect */ public void setGradient(boolean gradient){ if(mIsGradient != gradient){ mIsGradient = gradient; } } public boolean getGradient(){ return mIsGradient; } /** * when segment changed, * mOnSegmentChangedListener.onSegmentChanged(newSelectedIndex) will be triggered * @param listener OnSegmentChangedListener */ public void setOnSegmentChangedListener(OnSegmentChangedListener listener){ mOnSegmentChangedListener = listener; } public void update(){ invalidate(); } private float getTextCenterYOffset(Paint.FontMetrics fontMetrics){ if(fontMetrics == null) return 0; return Math.abs(fontMetrics.top + fontMetrics.bottom)/2; } private String[] convertCharSequenceToString(CharSequence[] csArray){ if(csArray == null) return null; String[] sArray = new String[csArray.length]; for(int i = 0; i < csArray.length; i++){ sArray[i] = csArray[i].toString(); } return sArray; } private void assertTextsValid(String[] texts){ if(texts == null || texts.length < 2){ throw new IllegalArgumentException("SegmentControlView's content text array'length should larger than 1"); } } private boolean checkViewPagerOnPageChangeListener(ViewPager viewPager){ if(viewPager == null) return false; Field field = null; try { field = ViewPager.class.getDeclaredField("mOnPageChangeListener"); if(field == null) return false; field.setAccessible(true); Object o = field.get(viewPager); if(o != null && o instanceof InternalViewPagerListener){ return true; } } catch (Exception e) { return false; } return false; } private int getTouchedIndex(float x, float y){ if(!rectF.contains(x, y)){ return -1; } for(int i = 0; i < mTexts.length; i++){ if(rectF.left + i * unitWidth <= x && x < rectF.left + (i + 1) * unitWidth){ return i; } } return -1; } private boolean checkIfEqual(String[] a, String[] b){ if(a == null && b == null){ return true; } if(a != null){ if(b == null){ return false; } if(a.length != b.length){ return false; } for(int i = 0; i < a.length; i++){ if(a[i] == null && b[i] == null){ continue; } if(a[i] != null && a[i].equals(b[i])){ continue; }else{ return false; } } return true; } return false; } private int measureWidth(int measureSpec, Paint paint) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { int maxWidth = 0; int maxWidthItem = getMaxWidthOfTextArray(mTexts, paint); maxWidth = (maxWidthItem + 2 * mSegmentPaddingHorizontal + 2 * mFrameWidth) * mTexts.length; if(maxWidth < 2 * mFrameCornerRadius){ maxWidth = 2 * mFrameCornerRadius; } result = this.getPaddingLeft() + this.getPaddingRight() + maxWidth;//MeasureSpec.UNSPECIFIED if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } private int measureHeight(int measureSpec, Paint paint) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { int maxHeight = 0; int maxHeightItem = getMaxHeightOfTextArray(mTexts, paint); maxHeight = maxHeightItem + 2 * mSegmentPaddingVertical + 2 * mFrameWidth; if(maxHeight < 2 * mFrameCornerRadius){ maxHeight = 2 * mFrameCornerRadius; } result = this.getPaddingTop() + this.getPaddingBottom() + maxHeight;//MeasureSpec.UNSPECIFIED if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } private int getMaxWidthOfTextArray(String[] array, Paint paint){ if(array == null){ return 0; } int maxWidth = 0; for(String item : array){ if(item != null){ int itemWidth = getTextWidth(item, paint); maxWidth = Math.max(itemWidth, maxWidth); } } return maxWidth; } private int getMaxHeightOfTextArray(String[] array, Paint paint){ if(array == null){ return 0; } int maxHeight = 0; for(String item : array){ if(item != null){ int itemHeight = getTextHeight(item, paint); maxHeight = Math.max(itemHeight, maxHeight); } } return maxHeight; } private void drawBackgroundAndFrameAndText(Canvas canvas){ int curBackgroundColor = 0; int curTextColor = 0; for(int i = 0; i < mTexts.length; i++){ float left = rectF.left + unitWidth * i; pathFrame.reset(); if(i == 0){ pathFrame.moveTo(rectF.left, rectF.top + mFrameCornerRadius); rectFArc.offsetTo(rectF.left, rectF.top); pathFrame.arcTo(rectFArc, 180, 90); pathFrame.lineTo(rectF.left + unitWidth, rectF.top); pathFrame.lineTo(rectF.left + unitWidth, rectF.bottom); pathFrame.lineTo(rectF.left + mFrameCornerRadius, rectF.bottom); rectFArc.offsetTo(rectF.left, rectF.bottom - 2 * mFrameCornerRadius); pathFrame.arcTo(rectFArc, 90, 90); }else if(i == (mTexts.length - 1)){ pathFrame.moveTo(rectF.left + i * unitWidth, rectF.top); pathFrame.lineTo(rectF.right - mFrameCornerRadius, rectF.top); rectFArc.offsetTo(rectF.right - 2 * mFrameCornerRadius, rectF.top); pathFrame.arcTo(rectFArc, 270, 90); pathFrame.lineTo(rectF.right, rectF.bottom - mFrameCornerRadius); rectFArc.offsetTo(rectF.right - 2 * mFrameCornerRadius, rectF.bottom - 2 * mFrameCornerRadius); pathFrame.arcTo(rectFArc, 0, 90); pathFrame.lineTo(rectF.left + i * unitWidth, rectF.bottom); }else{ pathFrame.moveTo(left, rectF.top); pathFrame.lineTo(left + unitWidth, rectF.top); pathFrame.lineTo(left + unitWidth, rectF.bottom); pathFrame.lineTo(left, rectF.bottom); } pathFrame.close(); if(!mIsGradient){ if(i == mSelectedIndex){ curBackgroundColor = mColorBackgroundSelected; curTextColor = mColorTextSelected; }else{ curBackgroundColor = mColorBackgroundNormal; curTextColor = mColorTextNormal; } } if(mIsGradient){ if(viewPagerPositionOffset != 0f){ if(i == viewPagerPosition){ curBackgroundColor = getEvaluateColor(viewPagerPositionOffset, mColorBackgroundSelected, mColorBackgroundNormal); curTextColor = getEvaluateColor(viewPagerPositionOffset, mColorTextSelected, mColorTextNormal); }else if(i == viewPagerPosition + 1){ curBackgroundColor = getEvaluateColor(viewPagerPositionOffset, mColorBackgroundNormal, mColorBackgroundSelected); curTextColor = getEvaluateColor(viewPagerPositionOffset, mColorTextNormal, mColorTextSelected); }else{ curBackgroundColor = mColorBackgroundNormal; curTextColor = mColorTextNormal; } }else{ if(i == mSelectedIndex){ curBackgroundColor = mColorBackgroundSelected; curTextColor = mColorTextSelected; }else{ curBackgroundColor = mColorBackgroundNormal; curTextColor = mColorTextNormal; } } } paintBackground.setColor(curBackgroundColor); if(curTouchedIndex == i){ paintBackground.setColor(getDarkColor(curBackgroundColor, TOUCHED_BACKGROUND_DARK_COEFFICIENT)); } canvas.drawPath(pathFrame, paintBackground); canvas.drawPath(pathFrame, paintFrame); paintText.setColor(curTextColor); canvas.drawText(mTexts[i], left + unitWidth / 2,rectF.centerY() + textCenterYOffset, paintText); } } private int viewPagerPosition = -1; private float viewPagerPositionOffset = 0f; private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(mIsGradient){ mSelectedIndex = position; viewPagerPosition = position; viewPagerPositionOffset = positionOffset; invalidate(); } } @Override public void onPageScrollStateChanged(int state) { } @Override public void onPageSelected(int position) { if(mIsGradient){ }else{ SegmentControlView.this.setSelectedIndex(position); } } } private int getDarkColor(int color, float darkCoefficient){ int a = (color & 0xff000000) >>> 24; int r = (color & 0x00ff0000) >>> 16; int g = (color & 0x0000ff00) >>> 8; int b = (color & 0x000000ff) >>> 0; r = (int)(r * darkCoefficient); g = (int)(g * darkCoefficient); b = (int)(b * darkCoefficient); return a << 24 | r << 16 | g << 8 | b; } private int getEvaluateColor(float fraction, int startColor, int endColor){ int a, r, g, b; int sA = (startColor & 0xff000000) >>> 24; int sR = (startColor & 0x00ff0000) >>> 16; int sG = (startColor & 0x0000ff00) >>> 8; int sB = (startColor & 0x000000ff) >>> 0; int eA = (endColor & 0xff000000) >>> 24; int eR = (endColor & 0x00ff0000) >>> 16; int eG = (endColor & 0x0000ff00) >>> 8; int eB = (endColor & 0x000000ff) >>> 0; a = (int)(sA + (eA - sA) * fraction); r = (int)(sR + (eR - sR) * fraction); g = (int)(sG + (eG - sG) * fraction); b = (int)(sB + (eB - sB) * fraction); return a << 24 | r << 16 | g << 8 | b; } private boolean isStringArrayEmpty(String[] array){ return (array == null || array.length == 0); } private static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } private int getTextWidth(String text, Paint paint){ if(!TextUtils.isEmpty(text)){ return (int)(paint.measureText(text) + 0.5f); } return -1; } private int getTextHeight(String text, Paint paint){ if(!TextUtils.isEmpty(text)){ Rect textBounds = new Rect(); paint.getTextBounds(text, 0, text.length(), textBounds); return textBounds.height(); } return -1; } }项目地址:https://github.com/Carbs0126/AndroidSegmentControlView
2022年05月18日
87 阅读
0 评论
0 点赞
2021-11-04
kotlin使用android自带的TextSwitcher实现Textview的上下滚动
先看效果:布局:activi<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <View android:layout_marginTop="50dp" android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#ccc"/> <TextSwitcher android:id="@+id/text_switcher" android:layout_width="match_parent" android:layout_height="50dp"/> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#ccc"/> </androidx.constraintlayout.widget.ConstraintLayout>代码:MainActivity.ktpackage com.chinabm.android_demo import android.annotation.SuppressLint import android.content.Intent import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Message import android.text.TextUtils import android.view.Gravity import android.view.View import android.view.ViewGroup import android.widget.* import androidx.appcompat.app.AppCompatActivity import java.util.* import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { private val list: Array<String> = arrayOf( "这是第一条数据", "这是第二条数据", "这是第三条数据" ) private var mHandler: Handler? = null; private val UPDATE_TEXT = 0; private var task: TimerTask? = null; private var timer: Timer? = null; private var index = 0; override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initView(); mHandler = @SuppressLint("HandlerLeak") object : Handler() { override fun handleMessage(msg: Message) { super.handleMessage(msg) if (msg.what == UPDATE_TEXT) { text_switcher.setText(list.get(index)); } } } task = object : TimerTask() { override fun run() { index = ++index % list.size; mHandler?.sendEmptyMessage(UPDATE_TEXT); } }; timer = Timer(); if (task != null && timer != null) { timer?.schedule(task, Date(), 6000) } } private fun initView() { text_switcher.setFactory(ViewSwitcher.ViewFactory(function = { var textView: TextView = TextView(this); textView.setLayoutParams( FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) ); textView.setGravity(Gravity.LEFT); textView.setSingleLine(); textView.ellipsize = TextUtils.TruncateAt.END; textView.setTextSize(16F); textView.setTextColor(Color.WHITE); textView })); text_switcher.setText(list.get(0)); text_switcher.setInAnimation(this, R.anim.show); text_switcher.setOutAnimation(this, R.anim.hide); } }show动画:show.xml<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <translate android:duration="1000" android:fromXDelta="0%" android:fromYDelta="100%" android:interpolator="@android:anim/accelerate_interpolator" android:toXDelta="0%" android:toYDelta="0%"> </translate> <alpha android:duration="900" android:fromAlpha="0.0" android:toAlpha="1.0"/> </set> hide动画:hide.xml<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true"> <translate android:duration="1000" android:fromXDelta="0%" android:fromYDelta="0%" android:interpolator="@android:anim/accelerate_interpolator" android:toXDelta="0%" android:toYDelta="-100%"> </translate> <alpha android:duration="900" android:fromAlpha="1.0" android:toAlpha="0.0"/> </set> 两个动画文件需要放创建一个anim文件夹存放在res目录该文使用了kotlinx插件,需要了解的可以移步 《使用kotlinx书写android实践及常见问题处理》 。
2021年11月04日
469 阅读
0 评论
0 点赞
2021-11-04
使用kotlinx书写android实践及常见问题处理
Kotlin中找不到kotlinx解决方法在初始化Kotlin界面布局时需引用kotinx来绑定布局import kotlinx.android.synthetic.main.activity_money.*找不到kotlinx时在gradle文件中加入apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions'kotlinx书写android实践实现效果如上图,想要kotlinx实现上面的效果,以为很简单,但却碰到不少的坑,毕竟还没学过kotlinx语法,不过最终还是弄出来了,在这里记录下。准备工欲善其事必先利其器,所以我们首先要准备好环境,在这里我使用的是AS 3.0预览版,本身就支持kotlinx,使用2.x的要装插件支持,不过我觉得使用就使用3.0吧,毕竟是亲爹支持的,新建项目什么的省了,不知道的google下...开发MainActivity.ktimport android.os.Bundle import android.support.design.widget.Snackbar import android.support.v7.app.AppCompatActivity import android.view.Menu import android.view.MenuItem import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) supportFragmentManager.beginTransaction() .add(R.id.fragment, MainActivityFragment(), "MainActivityFragment") .commit(); fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } } override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu; this adds items to the action bar if it is present. menuInflater.inflate(R.menu.menu_main, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. return when (item.itemId) { R.id.action_settings -> true else -> super.onOptionsItemSelected(item) } } }这里要注意下,由于kotlinx与java的语法风格不一样,所以要特别注意:继承不再写extends,而是直接:表示;使用类里面的成员或方法时,直接 supportFragmentManager.beginTransaction(),而不再 getSupportFragmentManager().beginTransaction(),很简洁;获取控件时,没有了findViewById,而是直接使用id来引用,不过这里要注意,引入的时候记得引入 import kotlinx.android.synthetic.main.activity_main.* 这个。activity_main.xml<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.jhworks.rxjavademo.MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"/> </android.support.design.widget.AppBarLayout> <FrameLayout android:id="@+id/fragment" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" app:srcCompat="@android:drawable/ic_dialog_email"/> </android.support.design.widget.CoordinatorLayout>MainActivityFragment.mkimport android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import kotlinx.android.synthetic.main.fragment_main.* /** * A placeholder fragment containing a simple view. */ class MainActivityFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_main, container, false) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) recycler_view.layoutManager = LinearLayoutManager(context) val list = ArrayList<String>() var i: Int = 0 while (i < 10) { list.add("item---" + i) i++ } val adapter = ListAdapter(context) recycler_view.adapter = adapter adapter.setDataList(list) } }ListAdapter.ktimport android.content.Context import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.ViewGroup /** * @author LiaoJH * @VERSION 1.0 * email: 583125288@qq.com * @DESC TODO */ class ListAdapter(context: Context) : RecyclerView.Adapter<ListHolder>() { private var mLayoutInflate: LayoutInflater = LayoutInflater.from(context) private var mDataList: List<String>? = null fun setDataList(dataList: List<String>) { mDataList = dataList } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ListHolder { return ListHolder(mLayoutInflate.inflate(R.layout.list_item_layout, parent, false)) } override fun getItemCount(): Int { if (mDataList == null) return 0 else return mDataList?.size as Int } override fun onBindViewHolder(holder: ListHolder?, position: Int) { holder?.bindData(mDataList?.get(position) as String) } }list_item_layout.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/name_tv" android:layout_width="match_parent" android:layout_height="40dp" android:paddingLeft="20dp" android:gravity="center_vertical" android:text="Item" /> </LinearLayout>ListHolder.ktimport android.support.v7.widget.RecyclerView import android.view.View import android.widget.TextView /** * @author LiaoJH * @VERSION 1.0 * email: 583125288@qq.com * @DESC TODO */ class ListHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) { private var mNameTv: TextView = itemView?.findViewById(R.id.name_tv) as TextView fun bindData(name: String) { mNameTv.text = name } }这样就能实现开头的效果了,第一次使用kotlinx,还是有不少坑,在此记录下。参考资料:https://www.jianshu.com/p/07fb921e1248
2021年11月04日
232 阅读
0 评论
0 点赞