[feature] 通过反射调整间隙

This commit is contained in:
yangyakun
2022-10-17 18:04:11 +08:00
parent 2d7970bfa0
commit ebac03d7f2
5 changed files with 140 additions and 0 deletions

View File

@@ -115,6 +115,8 @@
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:text="----"
app:customGap="0.5"
app:useCustomGap="true"
android:textColor="@color/bus_p_line_name_color"
android:textSize="@dimen/bus_p_driver_number_plate_size"
android:textStyle="bold"

View File

@@ -18,6 +18,8 @@
android:layout_marginRight="@dimen/dp_60"
android:textColor="@color/bus_p_station_txt_color"
android:layout_marginLeft="@dimen/dp_90"
app:customGap="0.5"
app:useCustomGap="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/bus_p_tag"
app:layout_constraintTop_toBottomOf="@+id/bus_p_cur_arrow_bg"/>

View File

@@ -0,0 +1,37 @@
package com.mogo.och.common.module.utils;
import android.text.TextUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
public class FieldUtils {
public static Field getDeclaredField(final Class<?> cls, final String fieldName, final
boolean forceAccess) {
if (cls == null || TextUtils.isEmpty(fieldName)) {
return null;
}
try {
// only consider the specified class by using getDeclaredField()
final Field field = cls.getDeclaredField(fieldName);
if (!isAccessible(field)) {
if (forceAccess) {
field.setAccessible(true);
} else {
return null;
}
}
return field;
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
private static boolean isAccessible(final Member m) {
return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
}
}

View File

@@ -1,12 +1,31 @@
package com.mogo.och.common.module.wigets;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.ViewDebug;
import android.widget.TextView;
import com.mogo.och.common.module.R;
import com.mogo.och.common.module.utils.FieldUtils;
import java.lang.reflect.Field;
public class MarqueeTextView extends androidx.appcompat.widget.AppCompatTextView{
/**
* 上一次设置的时间,用于过滤多余操作
*/
private long mLastSetTime;
/**
* 自定义 gap
*/
private float mCustomGap = 0.3f;
/**
* 是否使用自定义 gap
*/
private boolean mUseCustomGap;
public MarqueeTextView(Context context) {
this(context, null);
@@ -18,6 +37,11 @@ public class MarqueeTextView extends androidx.appcompat.widget.AppCompatTextView
public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MarqueeTextView);
mCustomGap = typedArray.getFloat(R.styleable.MarqueeTextView_customGap, mCustomGap);
mUseCustomGap = typedArray.getBoolean(R.styleable.MarqueeTextView_useCustomGap, false);
typedArray.recycle();
}
@Override
@@ -32,4 +56,74 @@ public class MarqueeTextView extends androidx.appcompat.widget.AppCompatTextView
return true;
}
@Override
public void invalidate() {
reflectToChangeGap();
super.invalidate();
}
private void reflectToChangeGap() {
if (!mUseCustomGap) {
return;
}
if (System.currentTimeMillis() - mLastSetTime < 1000) {
// 1s 内不重新设置,过滤多余操作
return;
}
try {
Class marqueClass = null;
Class[] innerClazz = TextView.class.getDeclaredClasses();
for (Class clazz : innerClazz) {
if ("Marquee".equals(clazz.getSimpleName())) {
marqueClass = clazz;
}
}
if (marqueClass == null) {
return;
}
Field field1 = FieldUtils.getDeclaredField(marqueClass, "mGhostStart", true);
if (field1 == null) {
return;
}
final int textWidth = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
final float lineWidth = getLayout().getLineWidth(0);
final float gap = mCustomGap*textWidth;
float ghostStart = lineWidth - textWidth + gap;
float maxScroll = ghostStart + textWidth;
float ghostOffset = lineWidth + gap;
float maxFadeScroll = ghostStart + lineWidth + lineWidth;
final Field field = FieldUtils.getDeclaredField(TextView.class, "mMarquee", true);
if (field != null) {
Object mMarque = field.get(this);
if (mMarque != null) {
mLastSetTime = System.currentTimeMillis();
float mGhostStart = (float) field1.get(mMarque);
if (mGhostStart != ghostStart) {
// 需要设置的 mGhostStart 与当前 ghostStart 不相等时才去设置
Field field2 = FieldUtils.getDeclaredField(marqueClass, "mMaxScroll", true);
Field field3 = FieldUtils.getDeclaredField(marqueClass, "mGhostOffset", true);
Field field4 = FieldUtils.getDeclaredField(marqueClass, "mMaxFadeScroll", true);
if (field2 == null || field3 == null || field4 == null) {
return;
}
field1.set(mMarque, ghostStart);
field2.set(mMarque, maxScroll);
field3.set(mMarque, ghostOffset);
field4.set(mMarque, maxFadeScroll);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -52,4 +52,9 @@
<attr name="och_realtime_end_color" format="color" />
<attr name="och_realtime_radius" format="dimension" />
</declare-styleable>
<declare-styleable name="MarqueeTextView">
<attr name="customGap" format="float" />
<attr name="useCustomGap" format="boolean"/>
</declare-styleable>
</resources>