Updates edittext views with corner rounded

This commit is contained in:
h4h13 2019-03-28 19:02:53 +05:30
parent 7a42723b9e
commit f9f30c8387
46 changed files with 1127 additions and 1286 deletions

1
library/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

41
library/build.gradle Normal file
View file

@ -0,0 +1,41 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation "androidx.core:core-ktx:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
mavenCentral()
}

21
library/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,26 @@
package com.lauzy.freedom.library;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.lauzy.freedom.library.test", appContext.getPackageName());
}
}

View file

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lauzy.freedom.library"/>

View file

@ -0,0 +1,30 @@
package com.lauzy.freedom.library;
/**
* Desc : 歌词实体
* Author : Lauzy
* Date : 2017/10/13
* Blog : http://www.jianshu.com/u/e76853f863a9
* Email : freedompaladin@gmail.com
*/
public class Lrc {
private long time;
private String text;
public void setTime(long time) {
this.time = time;
}
public void setText(String text) {
this.text = text;
}
public long getTime() {
return time;
}
public String getText() {
return text;
}
}

View file

@ -0,0 +1,137 @@
package com.lauzy.freedom.library;
import android.content.Context;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Desc : 歌词解析
* Author : Lauzy
* Date : 2017/10/13
* Blog : http://www.jianshu.com/u/e76853f863a9
* Email : freedompaladin@gmail.com
*/
public class LrcHelper {
private static final String CHARSET = "utf-8";
//[03:56.00][03:18.00][02:06.00][01:07.00]原谅我这一生不羁放纵爱自由
private static final String LINE_REGEX = "((\\[\\d{2}:\\d{2}\\.\\d{2}])+)(.*)";
private static final String TIME_REGEX = "\\[(\\d{2}):(\\d{2})\\.(\\d{2})]";
public static List<Lrc> parseLrcFromAssets(Context context, String fileName) {
try {
return parseInputStream(context.getResources().getAssets().open(fileName));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static List<Lrc> parseLrcFromFile(File file) {
try {
return parseInputStream(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
private static List<Lrc> parseInputStream(InputStream inputStream) {
List<Lrc> lrcs = new ArrayList<>();
InputStreamReader isr = null;
BufferedReader br = null;
try {
isr = new InputStreamReader(inputStream, CHARSET);
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
List<Lrc> lrcList = parseLrc(line);
if (lrcList != null && lrcList.size() != 0) {
lrcs.addAll(lrcList);
}
}
sortLrcs(lrcs);
return lrcs;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (isr != null) {
isr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
return lrcs;
}
private static void sortLrcs(List<Lrc> lrcs) {
Collections.sort(lrcs, new Comparator<Lrc>() {
@Override
public int compare(Lrc o1, Lrc o2) {
return (int) (o1.getTime() - o2.getTime());
}
});
}
private static List<Lrc> parseLrc(String lrcLine) {
if (lrcLine.trim().isEmpty()) {
return null;
}
List<Lrc> lrcs = new ArrayList<>();
Matcher matcher = Pattern.compile(LINE_REGEX).matcher(lrcLine);
if (!matcher.matches()) {
return null;
}
String time = matcher.group(1);
String content = matcher.group(3);
Matcher timeMatcher = Pattern.compile(TIME_REGEX).matcher(time);
while (timeMatcher.find()) {
String min = timeMatcher.group(1);
String sec = timeMatcher.group(2);
String mil = timeMatcher.group(3);
Lrc lrc = new Lrc();
if (content != null && content.length() != 0) {
lrc.setTime(Long.parseLong(min) * 60 * 1000 + Long.parseLong(sec) * 1000
+ Long.parseLong(mil) * 10);
lrc.setText(content);
lrcs.add(lrc);
}
}
return lrcs;
}
public static String formatTime(long time) {
int min = (int) (time / 60000);
int sec = (int) (time / 1000 % 60);
return adjustFormat(min) + ":" + adjustFormat(sec);
}
private static String adjustFormat(int time) {
if (time < 10) {
return "0" + time;
}
return time + "";
}
}

View file

@ -0,0 +1,620 @@
package com.lauzy.freedom.library
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.Looper
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewConfiguration
import android.view.animation.DecelerateInterpolator
import android.widget.OverScroller
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
import java.util.*
/**
* Desc : 歌词
* Author : Lauzy
* Date : 2017/10/13
* Blog : http://www.jianshu.com/u/e76853f863a9
* Email : freedompaladin@gmail.com
*/
class LrcView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
private var mLrcData: MutableList<Lrc>? = null
private var mTextPaint: TextPaint? = null
private var mDefaultContent: String? = null
private var mCurrentLine: Int = 0
private var mOffset: Float = 0.toFloat()
private var mLastMotionX: Float = 0.toFloat()
private var mLastMotionY: Float = 0.toFloat()
private var mScaledTouchSlop: Int = 0
private var mOverScroller: OverScroller? = null
private var mVelocityTracker: VelocityTracker? = null
private var mMaximumFlingVelocity: Int = 0
private var mMinimumFlingVelocity: Int = 0
private var mLrcTextSize: Float = 0.toFloat()
private var mLrcLineSpaceHeight: Float = 0.toFloat()
private var mTouchDelay: Int = 0
private var mNormalColor: Int = 0
private var mCurrentPlayLineColor: Int = 0
private var mNoLrcTextSize: Float = 0.toFloat()
private var mNoLrcTextColor: Int = 0
//是否拖拽中否的话响应onClick事件
private var isDragging: Boolean = false
//用户开始操作
private var isUserScroll: Boolean = false
private var isAutoAdjustPosition = true
private var mPlayDrawable: Drawable? = null
private var isShowTimeIndicator: Boolean = false
private var mPlayRect: Rect? = null
private var mIndicatorPaint: Paint? = null
private var mIndicatorLineWidth: Float = 0.toFloat()
private var mIndicatorTextSize: Float = 0.toFloat()
private var mCurrentIndicateLineTextColor: Int = 0
private var mIndicatorLineColor: Int = 0
private var mIndicatorMargin: Float = 0.toFloat()
private var mIconLineGap: Float = 0.toFloat()
private var mIconWidth: Float = 0.toFloat()
private var mIconHeight: Float = 0.toFloat()
private var isEnableShowIndicator = true
private var mIndicatorTextColor: Int = 0
private var mIndicatorTouchDelay: Int = 0
private val mLrcMap = HashMap<String, StaticLayout>()
private val mHideIndicatorRunnable = Runnable {
isShowTimeIndicator = false
invalidateView()
}
private val mStaticLayoutHashMap = HashMap<String, StaticLayout>()
private val mScrollRunnable = Runnable {
isUserScroll = false
scrollToPosition(mCurrentLine)
}
private var mOnPlayIndicatorLineListener: OnPlayIndicatorLineListener? = null
private val lrcWidth: Int
get() = width - paddingLeft - paddingRight
private val lrcHeight: Int
get() = height
private val isLrcEmpty: Boolean
get() = mLrcData == null || lrcCount == 0
private val lrcCount: Int
get() = mLrcData!!.size
//itemOffset 和 mOffset 最小即当前位置
val indicatePosition: Int
get() {
var pos = 0
var min = java.lang.Float.MAX_VALUE
for (i in mLrcData!!.indices) {
val offsetY = getItemOffsetY(i)
val abs = Math.abs(offsetY - mOffset)
if (abs < min) {
min = abs
pos = i
}
}
return pos
}
var playDrawable: Drawable?
get() = mPlayDrawable
set(playDrawable) {
mPlayDrawable = playDrawable
mPlayDrawable!!.bounds = mPlayRect!!
invalidateView()
}
init {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.LrcView)
mLrcTextSize = typedArray.getDimension(R.styleable.LrcView_lrcTextSize, sp2px(context, 15f).toFloat())
mLrcLineSpaceHeight = typedArray.getDimension(R.styleable.LrcView_lrcLineSpaceSize, dp2px(context, 20f).toFloat())
mTouchDelay = typedArray.getInt(R.styleable.LrcView_lrcTouchDelay, 3500)
mIndicatorTouchDelay = typedArray.getInt(R.styleable.LrcView_indicatorTouchDelay, 2500)
mNormalColor = typedArray.getColor(R.styleable.LrcView_lrcNormalTextColor, Color.GRAY)
mCurrentPlayLineColor = typedArray.getColor(R.styleable.LrcView_lrcCurrentTextColor, Color.BLUE)
mNoLrcTextSize = typedArray.getDimension(R.styleable.LrcView_noLrcTextSize, dp2px(context, 20f).toFloat())
mNoLrcTextColor = typedArray.getColor(R.styleable.LrcView_noLrcTextColor, Color.BLACK)
mIndicatorLineWidth = typedArray.getDimension(R.styleable.LrcView_indicatorLineHeight, dp2px(context, 0.5f).toFloat())
mIndicatorTextSize = typedArray.getDimension(R.styleable.LrcView_indicatorTextSize, sp2px(context, 13f).toFloat())
mIndicatorTextColor = typedArray.getColor(R.styleable.LrcView_indicatorTextColor, Color.GRAY)
mCurrentIndicateLineTextColor = typedArray.getColor(R.styleable.LrcView_currentIndicateLrcColor, Color.GRAY)
mIndicatorLineColor = typedArray.getColor(R.styleable.LrcView_indicatorLineColor, Color.GRAY)
mIndicatorMargin = typedArray.getDimension(R.styleable.LrcView_indicatorStartEndMargin, dp2px(context, 5f).toFloat())
mIconLineGap = typedArray.getDimension(R.styleable.LrcView_iconLineGap, dp2px(context, 3f).toFloat())
mIconWidth = typedArray.getDimension(R.styleable.LrcView_playIconWidth, dp2px(context, 20f).toFloat())
mIconHeight = typedArray.getDimension(R.styleable.LrcView_playIconHeight, dp2px(context, 20f).toFloat())
mPlayDrawable = typedArray.getDrawable(R.styleable.LrcView_playIcon)
mPlayDrawable = if (mPlayDrawable == null) ContextCompat.getDrawable(context, R.drawable.play_icon) else mPlayDrawable
typedArray.recycle()
setupConfigs(context)
}
private fun setupConfigs(context: Context) {
mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
mMaximumFlingVelocity = ViewConfiguration.get(context).scaledMaximumFlingVelocity
mMinimumFlingVelocity = ViewConfiguration.get(context).scaledMinimumFlingVelocity
mOverScroller = OverScroller(context, DecelerateInterpolator())
mOverScroller!!.setFriction(0.1f)
// ViewConfiguration.getScrollFriction(); 默认摩擦力 0.015f
mTextPaint = TextPaint()
mTextPaint!!.apply {
isAntiAlias = true
textAlign = Paint.Align.LEFT
textSize = mLrcTextSize
typeface = ResourcesCompat.getFont(context, R.font.circular)
}
mDefaultContent = DEFAULT_CONTENT
mIndicatorPaint = Paint()
mIndicatorPaint!!.isAntiAlias = true
mIndicatorPaint!!.strokeWidth = mIndicatorLineWidth
mIndicatorPaint!!.color = mIndicatorLineColor
mPlayRect = Rect()
mIndicatorPaint!!.textSize = mIndicatorTextSize
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (changed) {
mPlayRect!!.left = mIndicatorMargin.toInt()
mPlayRect!!.top = (height / 2 - mIconHeight / 2).toInt()
mPlayRect!!.right = (mPlayRect!!.left + mIconWidth).toInt()
mPlayRect!!.bottom = (mPlayRect!!.top + mIconHeight).toInt()
mPlayDrawable!!.bounds = mPlayRect!!
}
}
fun setLrcData(lrcData: MutableList<Lrc>) {
resetView(DEFAULT_CONTENT)
mLrcData = lrcData
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (isLrcEmpty) {
drawEmptyText(canvas)
return
}
val indicatePosition = indicatePosition
mTextPaint!!.textSize = mLrcTextSize
mTextPaint!!.textAlign = Paint.Align.LEFT
var y = (lrcHeight / 2).toFloat()
val x = dip2px(context, 16f).toFloat()
for (i in 0 until lrcCount) {
if (i > 0) {
y += (getTextHeight(i - 1) + getTextHeight(i)) / 2 + mLrcLineSpaceHeight
}
if (mCurrentLine == i) {
mTextPaint!!.color = mCurrentPlayLineColor
} else if (indicatePosition == i && isShowTimeIndicator) {
mTextPaint!!.color = mCurrentIndicateLineTextColor
} else {
mTextPaint!!.color = mNormalColor
}
drawLrc(canvas, x, y, i)
}
if (isShowTimeIndicator) {
mPlayDrawable!!.draw(canvas)
val time = mLrcData!![indicatePosition].time
val timeWidth = mIndicatorPaint!!.measureText(LrcHelper.formatTime(time))
mIndicatorPaint!!.color = mIndicatorLineColor
canvas.drawLine(mPlayRect!!.right + mIconLineGap, (height / 2).toFloat(),
width - timeWidth * 1.3f, (height / 2).toFloat(), mIndicatorPaint!!)
val baseX = (width - timeWidth * 1.1f).toInt()
val baseline = (height / 2).toFloat() - (mIndicatorPaint!!.descent() - mIndicatorPaint!!.ascent()) / 2 - mIndicatorPaint!!.ascent()
mIndicatorPaint!!.color = mIndicatorTextColor
canvas.drawText(LrcHelper.formatTime(time), baseX.toFloat(), baseline, mIndicatorPaint!!)
}
}
private fun dip2px(context: Context, dpVale: Float): Int {
val scale = context.resources.displayMetrics.density
return (dpVale * scale + 0.5f).toInt()
}
private fun drawLrc(canvas: Canvas, x: Float, y: Float, i: Int) {
val text = mLrcData!![i].text
var staticLayout: StaticLayout? = mLrcMap[text]
if (staticLayout == null) {
mTextPaint!!.textSize = mLrcTextSize
staticLayout = StaticLayout(text, mTextPaint, lrcWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
mLrcMap[text] = staticLayout
}
canvas.save()
canvas.translate(x, y - (staticLayout.height / 2).toFloat() - mOffset)
staticLayout.draw(canvas)
canvas.restore()
}
//中间空文字
private fun drawEmptyText(canvas: Canvas) {
mTextPaint!!.textAlign = Paint.Align.LEFT
mTextPaint!!.color = mNoLrcTextColor
mTextPaint!!.textSize = mNoLrcTextSize
canvas.save()
val staticLayout = StaticLayout(mDefaultContent, mTextPaint,
lrcWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
val margin = dip2px(context, 16f).toFloat();
canvas.translate(margin, margin)
staticLayout.draw(canvas)
canvas.restore()
}
fun updateTime(time: Long) {
if (isLrcEmpty) {
return
}
val linePosition = getUpdateTimeLinePosition(time)
if (mCurrentLine != linePosition) {
mCurrentLine = linePosition
if (isUserScroll) {
invalidateView()
return
}
ViewCompat.postOnAnimation(this@LrcView, mScrollRunnable)
}
}
private fun getUpdateTimeLinePosition(time: Long): Int {
var linePos = 0
for (i in 0 until lrcCount) {
val lrc = mLrcData!![i]
if (time >= lrc.time) {
if (i == lrcCount - 1) {
linePos = lrcCount - 1
} else if (time < mLrcData!![i + 1].time) {
linePos = i
break
}
}
}
return linePos
}
private fun scrollToPosition(linePosition: Int) {
val scrollY = getItemOffsetY(linePosition)
val animator = ValueAnimator.ofFloat(mOffset, scrollY)
animator.addUpdateListener { animation ->
mOffset = animation.animatedValue as Float
invalidateView()
}
animator.duration = 300
animator.start()
}
private fun getItemOffsetY(linePosition: Int): Float {
var tempY = 0f
for (i in 1..linePosition) {
tempY += (getTextHeight(i - 1) + getTextHeight(i)) / 2 + mLrcLineSpaceHeight
}
return tempY
}
private fun getTextHeight(linePosition: Int): Float {
val text = mLrcData!![linePosition].text
var staticLayout: StaticLayout? = mStaticLayoutHashMap[text]
if (staticLayout == null) {
mTextPaint!!.textSize = mLrcTextSize
staticLayout = StaticLayout(text, mTextPaint,
lrcWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
mStaticLayoutHashMap[text] = staticLayout
}
return staticLayout.height.toFloat()
}
private fun overScrolled(): Boolean {
return mOffset > getItemOffsetY(lrcCount - 1) || mOffset < 0
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (isLrcEmpty) {
return super.onTouchEvent(event)
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain()
}
mVelocityTracker!!.addMovement(event)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
removeCallbacks(mScrollRunnable)
removeCallbacks(mHideIndicatorRunnable)
if (!mOverScroller!!.isFinished) {
mOverScroller!!.abortAnimation()
}
mLastMotionX = event.x
mLastMotionY = event.y
isUserScroll = true
isDragging = false
}
MotionEvent.ACTION_MOVE -> {
var moveY = event.y - mLastMotionY
if (Math.abs(moveY) > mScaledTouchSlop) {
isDragging = true
isShowTimeIndicator = isEnableShowIndicator
}
if (isDragging) {
// if (mOffset < 0) {
// mOffset = Math.max(mOffset, -getTextHeight(0) - mLrcLineSpaceHeight);
// }
val maxHeight = getItemOffsetY(lrcCount - 1)
// if (mOffset > maxHeight) {
// mOffset = Math.min(mOffset, maxHeight + getTextHeight(getLrcCount() - 1) + mLrcLineSpaceHeight);
// }
if (mOffset < 0 || mOffset > maxHeight) {
moveY /= 3.5f
}
mOffset -= moveY
mLastMotionY = event.y
invalidateView()
}
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
if (!isDragging && (!isShowTimeIndicator || !onClickPlayButton(event))) {
isShowTimeIndicator = false
invalidateView()
performClick()
}
handleActionUp(event)
}
}
// return isDragging || super.onTouchEvent(event);
return true
}
private fun handleActionUp(event: MotionEvent) {
if (isEnableShowIndicator) {
ViewCompat.postOnAnimationDelayed(this@LrcView, mHideIndicatorRunnable, mIndicatorTouchDelay.toLong())
}
if (isShowTimeIndicator && mPlayRect != null && onClickPlayButton(event)) {
isShowTimeIndicator = false
invalidateView()
if (mOnPlayIndicatorLineListener != null) {
mOnPlayIndicatorLineListener!!.onPlay(mLrcData!![indicatePosition].time,
mLrcData!![indicatePosition].text)
}
}
if (overScrolled() && mOffset < 0) {
scrollToPosition(0)
if (isAutoAdjustPosition) {
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
}
return
}
if (overScrolled() && mOffset > getItemOffsetY(lrcCount - 1)) {
scrollToPosition(lrcCount - 1)
if (isAutoAdjustPosition) {
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
}
return
}
mVelocityTracker!!.computeCurrentVelocity(1000, mMaximumFlingVelocity.toFloat())
val YVelocity = mVelocityTracker!!.yVelocity
val absYVelocity = Math.abs(YVelocity)
if (absYVelocity > mMinimumFlingVelocity) {
mOverScroller!!.fling(0, mOffset.toInt(), 0, (-YVelocity).toInt(), 0,
0, 0, getItemOffsetY(lrcCount - 1).toInt(),
0, getTextHeight(0).toInt())
invalidateView()
}
releaseVelocityTracker()
if (isAutoAdjustPosition) {
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
}
}
private fun onClickPlayButton(event: MotionEvent): Boolean {
val left = mPlayRect!!.left.toFloat()
val right = mPlayRect!!.right.toFloat()
val top = mPlayRect!!.top.toFloat()
val bottom = mPlayRect!!.bottom.toFloat()
val x = event.x
val y = event.y
return (mLastMotionX > left && mLastMotionX < right && mLastMotionY > top
&& mLastMotionY < bottom && x > left && x < right && y > top && y < bottom)
}
override fun computeScroll() {
super.computeScroll()
if (mOverScroller!!.computeScrollOffset()) {
mOffset = mOverScroller!!.currY.toFloat()
invalidateView()
}
}
private fun releaseVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker!!.clear()
mVelocityTracker!!.recycle()
mVelocityTracker = null
}
}
fun resetView(defaultContent: String) {
if (mLrcData != null) {
mLrcData!!.clear()
}
mLrcMap.clear()
mStaticLayoutHashMap.clear()
mCurrentLine = 0
mOffset = 0f
isUserScroll = false
isDragging = false
mDefaultContent = defaultContent
removeCallbacks(mScrollRunnable)
invalidate()
}
override fun performClick(): Boolean {
return super.performClick()
}
fun dp2px(context: Context, dpVal: Float): Int {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.resources.displayMetrics).toInt()
}
fun sp2px(context: Context, spVal: Float): Int {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.resources.displayMetrics).toInt()
}
/**
* 暂停手动滑动歌词后不再自动回滚至当前播放位置
*/
fun pause() {
isAutoAdjustPosition = false
invalidateView()
}
/**
* 恢复继续自动回滚
*/
fun resume() {
isAutoAdjustPosition = true
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
invalidateView()
}
/*------------------Config-------------------*/
private fun invalidateView() {
if (Looper.getMainLooper().thread === Thread.currentThread()) {
invalidate()
} else {
postInvalidate()
}
}
fun setOnPlayIndicatorLineListener(onPlayIndicatorLineListener: OnPlayIndicatorLineListener) {
mOnPlayIndicatorLineListener = onPlayIndicatorLineListener
}
fun setEmptyContent(defaultContent: String) {
mDefaultContent = defaultContent
invalidateView()
}
fun setLrcTextSize(lrcTextSize: Float) {
mLrcTextSize = lrcTextSize
invalidateView()
}
fun setLrcLineSpaceHeight(lrcLineSpaceHeight: Float) {
mLrcLineSpaceHeight = lrcLineSpaceHeight
invalidateView()
}
fun setTouchDelay(touchDelay: Int) {
mTouchDelay = touchDelay
invalidateView()
}
fun setNormalColor(@ColorInt normalColor: Int) {
mNormalColor = normalColor
invalidateView()
}
fun setCurrentPlayLineColor(@ColorInt currentPlayLineColor: Int) {
mCurrentPlayLineColor = currentPlayLineColor
invalidateView()
}
fun setNoLrcTextSize(noLrcTextSize: Float) {
mNoLrcTextSize = noLrcTextSize
invalidateView()
}
fun setNoLrcTextColor(@ColorInt noLrcTextColor: Int) {
mNoLrcTextColor = noLrcTextColor
invalidateView()
}
fun setIndicatorLineWidth(indicatorLineWidth: Float) {
mIndicatorLineWidth = indicatorLineWidth
invalidateView()
}
fun setIndicatorTextSize(indicatorTextSize: Float) {
// mIndicatorTextSize = indicatorTextSize;
mIndicatorPaint!!.textSize = indicatorTextSize
invalidateView()
}
fun setCurrentIndicateLineTextColor(currentIndicateLineTextColor: Int) {
mCurrentIndicateLineTextColor = currentIndicateLineTextColor
invalidateView()
}
fun setIndicatorLineColor(indicatorLineColor: Int) {
mIndicatorLineColor = indicatorLineColor
invalidateView()
}
fun setIndicatorMargin(indicatorMargin: Float) {
mIndicatorMargin = indicatorMargin
invalidateView()
}
fun setIconLineGap(iconLineGap: Float) {
mIconLineGap = iconLineGap
invalidateView()
}
fun setIconWidth(iconWidth: Float) {
mIconWidth = iconWidth
invalidateView()
}
fun setIconHeight(iconHeight: Float) {
mIconHeight = iconHeight
invalidateView()
}
fun setEnableShowIndicator(enableShowIndicator: Boolean) {
isEnableShowIndicator = enableShowIndicator
invalidateView()
}
fun setIndicatorTextColor(indicatorTextColor: Int) {
mIndicatorTextColor = indicatorTextColor
invalidateView()
}
interface OnPlayIndicatorLineListener {
fun onPlay(time: Long, content: String)
}
companion object {
private const val DEFAULT_CONTENT = "Empty"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
<font
android:font="@font/circular_std_book"
android:fontStyle="normal"
android:fontWeight="400" />
<font
android:font="@font/circular_std_black"
android:fontWeight="900" />
</font-family>

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LrcView">
<attr name="lrcTextSize" format="dimension"/>
<attr name="lrcLineSpaceSize" format="dimension"/>
<attr name="lrcNormalTextColor" format="reference|color"/>
<attr name="lrcCurrentTextColor" format="reference|color"/>
<attr name="lrcTouchDelay" format="integer"/>
<attr name="noLrcTextSize" format="dimension"/>
<attr name="noLrcTextColor" format="reference|color"/>
<attr name="indicatorLineHeight" format="dimension"/>
<attr name="indicatorTextSize" format="dimension"/>
<attr name="indicatorTextColor" format="reference|color"/>
<attr name="currentIndicateLrcColor" format="reference|color"/>
<attr name="indicatorTouchDelay" format="integer"/>
<attr name="indicatorLineColor" format="reference|color"/>
<attr name="indicatorStartEndMargin" format="dimension"/>
<attr name="iconLineGap" format="dimension"/>
<attr name="playIconWidth" format="dimension"/>
<attr name="playIconHeight" format="dimension"/>
<attr name="playIcon" format="reference"/>
</declare-styleable>
</resources>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">library</string>
</resources>

View file

@ -0,0 +1,17 @@
package com.lauzy.freedom.library;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}