Android富文本的实现的几种方式

在Android开发过程中,最常见的富文本场景一般都是变色,点击跳转,或者局部变大,而我们实现的方式通常分为两种。

一种是Html的方式定义在string中,通过html标签变色,变大,通过占位符填充数据。一般常用于有国际化的需求。

另一种是CharSequence的setSpan设置自定义Span。功能更强大,细读也更细,便于精准操作。一般用于没有国际化需求的地方。

为什么有国际化相关的要求,是因为一般setSpan的方式都是添加或者根据索引替换对应的文本,如果国际化之后中英马等语言的顺序都变了,自然效果就不同了。当然也可以通过判断语言进行不同的操作。这是后话了。

一,Html的方式实现

1.1 占位符的处理

先看看string xml中如何处理占位符 %N代表第N个参数,如%3代表的是第三个参数; $是结束符;

<string name="string_test_1">学号:%1$d ;姓名:%2$s ;成绩:%3$.2f</string>

使用的时候:

String testStr = getResources().getString(R.string.string_test_1);
String result = String.format(testStr,1001,"张三",9.235);
System.out.println(result);

1.2 Html的占位符

和上面的差不多:

    <string name="purchase_points"><![CDATA[ <font color="#767676">Purchase with</font> 
    <font color="#FF5E75">%s</font><font color="#767676"> points?</font>]]></string>

使用:

    String formatPoints = PointFormatUtils.formatPoints(points);
    String result = String.format(getResources().getString(R.string.purchase_points),formatPoints);
    tv_message.setText(Html.fromHtml(result));

注:Html.fromHtml还分Android N的兼容处理,需要传入Model,不同的Model展示的效果有所不同,这里不做展开。其实效果大差不差。

实现效果:

结论:

能实现变色,简单的变大等简单功能,由于TextView不能解析更多的Html标签,由此还出现了一些库,让TextView支持更多标签,但是我们Android实现富文本本身就是小功能,还得依赖库支持更多标签也都用不上,得不偿失啊。

如果有一些自定义的需求,我们可以使用自定义标签 自定义标签的功能,例如Html中的自定义字体

1.3 自定义Html标签

先定义自定义字体的Span类

/**
 * 系统原生的TypefaceSpan只能使用原生的默认字体
 * 如果使用自定义的字体,通过这个来实现
 */
public class MyTypefaceSpan extends MetricAffectingSpan {
    private final Typeface typeface;

    public MyTypefaceSpan(final Typeface typeface) {
        this.typeface = typeface;
    }

    @Override
    public void updateDrawState(final TextPaint drawState) {
        apply(drawState);
    }

    @Override
    public void updateMeasureState(final TextPaint paint) {
        apply(paint);
    }

    private void apply(final Paint paint) {
        final Typeface oldTypeface = paint.getTypeface();
        final int oldStyle = oldTypeface != null ? oldTypeface.getStyle() : 0;
        int fakeStyle = oldStyle & ~typeface.getStyle();
        if ((fakeStyle & Typeface.BOLD) != 0) {
            paint.setFakeBoldText(true);
        }
        if ((fakeStyle & Typeface.ITALIC) != 0) {
            paint.setTextSkewX(-0.25f);
        }
        paint.setTypeface(typeface);
    }
}

自定义标签:

/**
 * Html的TextView标签解释
 * <face></face>
 */
public class TypeFaceLabel implements Html.TagHandler {
    private Typeface typeface;
    private int startIndex = 0;
    private int stopIndex = 0;

    public TypeFaceLabel(Typeface typeface) {
        this.typeface = typeface;
    }

    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
        if (tag.toLowerCase().equals("face")) {
            if (opening) {
                startIndex = output.length();
            } else {
                stopIndex = output.length();
                //使用的是自定义的字体来实现
                output.setSpan(new MyTypefaceSpan(typeface), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }
}

定义Xml并使用,注意自定义face标签

  String content = "<font color=\"#000000\">HR from </font>"  
                    "<face><font color=\"#0689FB\">"   item.employer_name   "</font></face>"  
                    "<font color=\"#000000\"> has viewed your resume.</font>";

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
          tv_resume_log_content.setText(Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY, null, new TypeFaceLabel(TypefaceUtil.getSFSemobold(mContext))));
        } else {
          tv_resume_log_content.setText(Html.fromHtml(content, null, new TypeFaceLabel(TypefaceUtil.getSFSemobold(mContext))));
    }

效果如下:

如果想实现其他的变大 下划线 中划线等Span效果,都可以通过自定义的Html标签 自定义Span实现相应的效果。

二,Span的几种实现方式

虽然通过Html的方式可以实现各种效果,但是定义的时候也太过复杂,各种定义Span 定义标签之类的,有没有更简单和直接的?

有,我们直接封装Span就行了。

2.1 java - SpanUtil

在Java中我们可以封装工具类一个如下:

/**
 * String字符串通过区间来改变颜色,大小,字体,下划线等
 */
public class SpanUtils {
    private static final SpanUtils ourInstance = new SpanUtils();
    public static SpanUtils getInstance() {
        return ourInstance;
    }

    private SpanUtils() {
    }

    /**
     * 变大变小
     */
    public CharSequence toSizeSpan(CharSequence charSequence, int start, int end, float scale) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new RelativeSizeSpan(scale),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    /**
     * 变色
     */
    public CharSequence toColorSpan(CharSequence charSequence, int start, int end, int color) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new ForegroundColorSpan(color),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    /**
     * 变背景色
     */
    public CharSequence toBackgroundColorSpan(CharSequence charSequence, int start, int end, int color) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new BackgroundColorSpan(color),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    private long mLastClickTime = 0;
    public static final int TIME_INTERVAL = 1000;

    /**
     * 可点击-带下划线
     */
    public CharSequence toClickSpan(CharSequence charSequence, int start, int end, int color, boolean needUnderLine, OnSpanClickListener listener) {

        SpannableString spannableString = new SpannableString(charSequence);

        ClickableSpan clickableSpan = new ClickableSpan() {
            @Override
            public void onClick(@NonNull View widget) {
                if (listener != null) {
                    //防止重复点击
                    if (System.currentTimeMillis() - mLastClickTime >= TIME_INTERVAL) {
                        //to do
                        listener.onClick(charSequence.subSequence(start, end));

                        mLastClickTime = System.currentTimeMillis();
                    }

                }
            }

            @Override
            public void updateDrawState(@NonNull TextPaint ds) {
                ds.setColor(color);
                ds.setUnderlineText(needUnderLine);
            }
        };

        spannableString.setSpan(
                clickableSpan,
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }

    public interface OnSpanClickListener {
        void onClick(CharSequence charSequence);
    }


    /**
     * 变成自定义的字体
     */
    public CharSequence toCustomTypeFaceSpan(CharSequence charSequence, int start, int end, Typeface typeface) {

        SpannableString spannableString = new SpannableString(charSequence);

        spannableString.setSpan(
                new MyTypefaceSpan(typeface),
                start,
                end,
                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

        return spannableString;
    }
}

2.2 kotlin扩展

/**
 * 将一段文字中指定range的文字改变大小
 * @param range 要改变大小的文字的范围
 * @param scale 缩放值,大于1,则比其他文字大;小于1,则比其他文字小;默认是1.5
 */
fun CharSequence.toSizeSpan(range: IntRange, scale: Float = 1.5f): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            RelativeSizeSpan(scale),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 将一段文字中指定range的文字改变前景色
 * @param range 要改变前景色的文字的范围
 * @param color 要改变的颜色,默认是红色
 */
fun CharSequence.toColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            ForegroundColorSpan(color),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 将一段文字中指定range的文字改变背景色
 * @param range 要改变背景色的文字的范围
 * @param color 要改变的颜色,默认是红色
 */
fun CharSequence.toBackgroundColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            BackgroundColorSpan(color),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 将一段文字中指定range的文字添加删除线
 * @param range 要添加删除线的文字的范围
 */
fun CharSequence.toStrikeThrougthSpan(range: IntRange): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            StrikethroughSpan(),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 将一段文字中指定range的文字添加颜色和点击事件
 * @param range 目标文字的范围
 */
fun CharSequence.toClickSpan(
    range: IntRange,
    color: Int = Color.RED,
    isUnderlineText: Boolean = false,
    clickAction: (() -> Unit)?
): CharSequence {
    return SpannableString(this).apply {
        val clickableSpan = object : ClickableSpan() {
            override fun onClick(widget: View) {
                clickAction?.invoke()
            }

            override fun updateDrawState(ds: TextPaint) {
                ds.color = color
                ds.isUnderlineText = isUnderlineText
            }
        }
        setSpan(clickableSpan, range.start, range.endInclusive, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
    }
}

/**
 * 将一段文字中指定range的文字添加style效果
 * @param range 要添加删除线的文字的范围
 */
fun CharSequence.toStyleSpan(style: Int = Typeface.BOLD, range: IntRange): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            StyleSpan(style),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

/**
 * 将一段文字中指定range的文字添加自定义效果
 * @param range 要添加删除线的文字的范围
 */
fun CharSequence.toCustomTypeFaceSpan(typeface: Typeface, range: IntRange): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            CustomTypefaceSpan(typeface),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}


/**
 * 将一段文字中指定range的文字添加自定义效果,可以设置对齐方式,可以设置margin
 * @param range
 */
fun CharSequence.toImageSpan(
    imageRes: Int,
    range: IntRange,
    verticalAlignment: Int = 0,  //默认底部  4是垂直居中
    maginLeft: Int = 0,
    marginRight: Int = 0,
    width: Int = 0,
    height: Int = 0
): CharSequence {
    return SpannableString(this).apply {
        setSpan(
            MiddleIMarginImageSpan(
                CommUtils.getDrawable(imageRes)
                    .apply {
                        setBounds(0, 0, if (width == 0) getIntrinsicWidth() else width, if (height == 0) getIntrinsicHeight() else height)
                    },
                verticalAlignment,
                maginLeft,
                marginRight
            ),
            range.start,
            range.endInclusive,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
    }
}

扩展方法的使用

  mBinding.tvTextSpan1.text = "演示一下appendXX方法的用法\n"
        mBinding.tvTextSpan1.appendSizeSpan("变大变大", 1.5f)
            .appendColorSpan("我要变色", color = Color.parseColor("#f0aafc"))
            .appendBackgroundColorSpan("我是有底色的", color = Color.parseColor("#cacee0"))
            .appendStrikeThrougthSpan("添加删除线哦哦哦哦")
            .appendClickSpan("来点我一下试试啊", isUnderlineText = true, clickAction = {
                toast("哎呀,您点到我了呢,嘿嘿")
            })
            .appendImageSpan(R.mipmap.ic_launcher)  //默认的大图什么都不加 默认在底部对齐
            .appendStyleSpan("我是粗体的") //可以是默认粗体 斜体等
            .appendImageSpan(R.mipmap.ic_launcher_round, 4, width = dp2px(35f), height = dp2px(35f))//4是居中的,限制Drawable
            .appendCustomTypeFaceSpan("Xiao mi Hua wei", TypefaceUtil.getSFFlower(mActivity))  //自定义字体文件
            //默认底部对齐,加左右margin
            .appendImageSpan(R.mipmap.iv_me_red_packet, maginLeft = dp2px(10f), marginRight = dp2px(10f))
            //添加删除线
            .appendStrikeThrougthSpan("添加删除线哦哦哦哦添加删除线哦哦哦哦")

效果:

2.3 kotlin DSL方式

如果是使用Kotlin的语言开发,那么还有更简单的DSL封装方式:

第一层的DSL接口

interface DslSpannableStringBuilder {
    //增加一段文字
    fun addText(text: String, method: (DslSpanBuilder.() -> Unit)? = null)

    //添加一个图标
    fun addImage(imageRes: Int, verticalAlignment: Int = 0, maginLeft: Int = 0, marginRight: Int = 0, width: Int = 0, height: Int = 0)
}

第一层的DSL接口实现

class DslSpannableStringBuilderImpl : DslSpannableStringBuilder {
    private val builder = SpannableStringBuilder()
    //添加文本
    override fun addText(text: String, method: (DslSpanBuilder.() -> Unit)?) {
        val spanBuilder = DslSpanBuilderImpl()
        method?.let { spanBuilder.it() }

        var charSeq: CharSequence = text

        spanBuilder.apply {
            if (issetColor) {
                charSeq = charSeq.toColorSpan(0..text.length, textColor)
            }
            if (issetBackground) {
                charSeq = charSeq.toBackgroundColorSpan(0..text.length, textBackgroundColor)
            }
            if (issetScale) {
                charSeq = charSeq.toSizeSpan(0..text.length, scaleSize)
            }
            if (isonClick) {
                charSeq = charSeq.toClickSpan(0..text.length, textColor, isuseUnderLine, onClick)
            }
            if (issetTypeface) {
                charSeq = charSeq.toCustomTypeFaceSpan(typefaces, 0..text.length)
            }
            if (issetStrikethrough) {
                charSeq = charSeq.toStrikeThrougthSpan(0..text.length)
            }

            builder.append(charSeq)
        }
    }

    //添加图标
    override fun addImage(imageRes: Int, verticalAlignment: Int, maginLeft: Int, marginRight: Int, width: Int, height: Int) {
        var charSeq: CharSequence = "1"
        charSeq = charSeq.toImageSpan(imageRes, 0..1, verticalAlignment, maginLeft, marginRight, width, height)
        builder.append(charSeq)
    }

    fun build(): SpannableStringBuilder {
        return builder
    }
}

第二层Text的DSL接口

interface DslSpanBuilder {
    //设置文字颜色
    fun setColor(color: Int = 0)

    //设置点击事件
    fun setClick(useUnderLine: Boolean = true, onClick: (() -> Unit)?)

    //设置缩放大小
    fun setScale(scale: Float = 1.0f)

    //设置自定义字体
    fun setTypeface(typeface: Typeface)

    //是否需要中划线
    fun setStrikethrough(isStrikethrough: Boolean = false)

    //设置背景
    fun setBackground(color: Int = Color.TRANSPARENT)
}

第二层Text的DSL接口实现

class DslSpanBuilderImpl : DslSpanBuilder {
    var issetColor = false
    var textColor: Int = Color.BLACK

    var isonClick = false
    var isuseUnderLine = false
    var onClick: (() -> Unit)? = null

    var issetScale = false
    var scaleSize = 1.0f

    var issetTypeface = false
    var typefaces: Typeface = Typeface.DEFAULT

    var issetStrikethrough = false

    var issetBackground = false
    var textBackgroundColor = 0

    override fun setColor(color: Int) {
        issetColor = true
        textColor = color
    }

    override fun setClick(useUnderLine: Boolean, onClick: (() -> Unit)?) {
        isonClick = true
        isuseUnderLine = useUnderLine
        this.onClick = onClick
    }

    override fun setScale(scale: Float) {
        issetScale = true
        scaleSize = scale
    }

    override fun setTypeface(typeface: Typeface) {
        issetTypeface = true
        typefaces = typeface
    }

    override fun setStrikethrough(isStrikethrough: Boolean) {
        issetStrikethrough = isStrikethrough
    }

    override fun setBackground(color: Int) {
        issetBackground = true
        textBackgroundColor = color
    }
}

创建TextVuew的扩展入口

//为 TextView 创建扩展函数,其参数为接口的扩展函数
fun TextView.buildSpannableString(init: DslSpannableStringBuilder.() -> Unit) {
    //具体实现类
    val spanStringBuilderImpl = DslSpannableStringBuilderImpl()
    spanStringBuilderImpl.init()
    movementMethod = LinkMovementMethod.getInstance()
    //通过实现类返回SpannableStringBuilder
    text = spanStringBuilderImpl.build()
}

使用:

    mBinding.tvTextSpan4.buildSpannableString {
            addText("我已详细阅读并同意")
            addText("测试红色的文字颜色") {
                setColor(Color.RED)
            }
            addText("测试白色文字加上灰色背景") {
                setColor(Color.WHITE)
                setBackground(Color.GRAY)
            }
            addText("测试文本变大了") {
                setColor(Color.DKGRAY)
                setScale(1.5f)
            }
            addImage(R.mipmap.ic_launcher)
            addText("测试可以点击的文本") {
                setClick(true) {
                    toast("点击文本拉啦啦")
                }
            }
            addImage(R.mipmap.ic_launcher_round, 5, dp2px(10f), dp2px(10f), dp2px(35f), dp2px(35f))
            addText("Test Custom Typeface Font is't Success?") {
                setTypeface(TypefaceUtil.getSFFlower(mActivity))
            }
            addText("测试中划线是否生效") {
                setStrikethrough(true)
            }
        }

效果:

总结

如果是顺序固定,效果复杂,那么可以用Span的方式。

如果顺序不固定(如国际化)那么可以使用Html的方式。

总的来说,两种方式都不算太难,都是些固定的代码。如果需求可以看源码。

到此这篇关于Android富文本实现的几种方式的文章就介绍到这了,更多相关Android富文本实现内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Android富文本实现的几种方式汇总的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部