开源UI组件推荐

开源UI组件推荐

【加载动画】 Android-SpinKit

https://github.com/ybq/Android-SpinKit

演示效果

img

Style:不同风格的加载动画,与上图一一对应

RotatingPlaneDoubleBounceWaveWanderingCubes
PulseChasingDotsThreeBounceCircle
CubeGridFadingCircleFoldingCubeRotatingCircle

使用方法

首先在项目的Gradle文件中导入如下依赖

dependencies {
   implementation 'com.github.ybq:Android-SpinKit:1.4.0'
}

xml文件中使用:

<com.github.ybq.android.spinkit.SpinKitView
    android:id="@+id/spin_kit"
    style="@style/SpinKitView.Large.Circle"//Circle样式
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    app:SpinKit_Color="@color/design_default_color_primary"//深蓝色 />

java代码中使用:

public class MainActivity extends AppCompatActivity{
    private Wave mWaveDrawable;
    private Circle mCircleDrawable;
    private ChasingDots mChasingDotsDrawable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //ProgressBar
        ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress);
        DoubleBounce doubleBounce = new DoubleBounce();
        doubleBounce.setBounds(0, 0, 100, 100);
        doubleBounce.setColor(Color.BLACK);
        progressBar.setIndeterminateDrawable(doubleBounce);

        //Button
        Button button = (Button) findViewById(R.id.button);
        mWaveDrawable = new Wave();
        mWaveDrawable.setBounds(0, 0, 100, 100);
        //noinspection deprecation
        mWaveDrawable.setColor(getResources().getColor(R.color.teal_700));
        button.setCompoundDrawables(mWaveDrawable, null, null, null);

        //TextView
        TextView textView = (TextView) findViewById(R.id.text);
        mCircleDrawable = new Circle();
        mCircleDrawable.setBounds(0, 0, 100, 100);
        mCircleDrawable.setColor(Color.WHITE);
        textView.setCompoundDrawables(null, null, mCircleDrawable, null);
        textView.setBackgroundColor(Color.GREEN);

        //ImageView
        ImageView imageView = (ImageView) findViewById(R.id.image);
        mChasingDotsDrawable = new ChasingDots();
        mChasingDotsDrawable.setColor(Color.WHITE);
        imageView.setImageDrawable(mChasingDotsDrawable);
        imageView.setBackgroundColor(Color.YELLOW);
    }
    @Override
    public void onResume() {
        super.onResume();
        mWaveDrawable.start();
        mCircleDrawable.start();
        mChasingDotsDrawable.start();
    }

    @Override
    public void onStop() {
        super.onStop();
        mWaveDrawable.stop();
        mCircleDrawable.stop();
        mChasingDotsDrawable.stop();
    }
}

Demo效果

【可视化图表】 AAChartCore

https://github.com/AAChartModel/AAChartCore

演示效果

折线图

在这里插入图片描述

条形图

在这里插入图片描述

雷达图

在这里插入图片描述

扇形图

在这里插入图片描述

使用方法

1.下载 Demo AAChartCoreDemo

2.将 Demo 中的名为AAChartCoreLib 文件夹assets 下的所有文件拖入至你的项目之中.

3.创建视图AAChartView

        <AAChartCore.AAChartConfiger.AAChartView
        android:id="@+id/AAChartView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
       AAChartView aaChartView = findViewById(R.id.AAChartView);

4.配置视图模型AAChartModel

  • 链式编程的方式配置 AAChartModel 模型对象属性
AAChartModel aaChartModel = new AAChartModel()
.chartType(AAChartType.Area)
.title("THE HEAT OF PROGRAMMING LANGUAGE")
.subtitle("Virtual Data")
.backgroundColor("#4b2b7f")
.categories(new String[]{"Java","Swift","Python","Ruby", "PHP","Go","C","C#","C++"})
.dataLabelsEnabled(false)
.yAxisGridLineWidth(0f)
.series(new AASeriesElement[]{
    new AASeriesElement()
    .name("Tokyo")
    .data(new Object[]{7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6}),
    new AASeriesElement()
    .name("NewYork")
    .data(new Object[]{0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5}),
    new AASeriesElement()
    .name("London")
    .data(new Object[]{0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0}),
    new AASeriesElement()
    .name("Berlin")
    .data(new Object[]{3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8})
});

5.绘制图形(创建 AAChartView 实例对象后,首次绘制图形调用此方法)

        /*图表视图对象调用图表模型对象,绘制最终图形*/
        aaChartView.aa_drawChartWithChartModel(aaChartModel);

DEMO

Demo示例

xml文件:

<com.desay.cma.AAChartCoreLib.AAChartCreator.AAChartView
    android:id="@+id/idm_score_line_view"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:layout_marginStart="25dp"
    android:layout_marginTop="0dp"
    android:layout_marginEnd="40dp"
    android:layout_marginBottom="0dp" />

java类中:

private void initCurveChart1(List<HistoryLogBean> historyLogBeanList) {

    Map<String, Object> linearGradientColor = AAGradientColor.linearGradient(
            AALinearGradientDirection.ToBottom,
            "rgba(255,0,0,0.5)",//红色, alpha 透明度 0.5
            "rgba(0,0,0,0)"//黑色, alpha 透明度 0
    );
    AAChartView aaChartView = findViewById(R.id.idm_score_line_view);
    AAChartModel aaChartModel = new AAChartModel();
    aaChartModel.chartType(AAChartType.Areaspline)
            .axesTextColor("#ffffff")
            .subtitleAlign("left")
            .backgroundColor("#141414")
            .gradientColorEnable(true)
            .markerRadius(3.0f)
            .dataLabelsEnabled(false)
            .yAxisGridLineWidth(1f)
            .xAxisGridLineWidth(1f)
            .series(new AASeriesElement[]{
                    new AASeriesElement()
                            .name("分数")
                            .data(new Object[][]{{1, 3.7f}, {2, 3.4f}, {3, 3.9f}, {4, 3.2f}, {5, 4.5f}})
                            .color(AAColor.rgbaColor(255, 0, 0, 0.8f))//猩红色, alpha 透明度 1
                            .fillColor(linearGradientColor)
            }).aa_toAAOptions();
    AAOptions aaOptions = aaChartModel.aa_toAAOptions();
    aaOptions.legend.enabled(false);
    aaOptions.yAxis.type("linear");
    aaOptions.xAxis.min = 1;
    aaOptions.yAxis.min = 1;
    aaOptions.tooltip.headerFormat("第 {point.x} 次<br>").pointFormat("分数:{point.y}");
    aaOptions.xAxis.lineWidth = 1.5;
    aaOptions.yAxis.lineWidth = 1.5;
    aaOptions.xAxis.tickWidth(0);
    aaOptions.yAxis.tickWidth(0);
    aaOptions.yAxis.labels.format("{value:.1f}");
    //设置表格线为虚线
    aaOptions.xAxis.gridLineDashStyle("Dash");
    aaOptions.yAxis.gridLineDashStyle("Dash");
    aaChartView.aa_drawChartWithChartOptions(aaOptions);
}

然后在初始化UI的方法中调用该方法即可。

Demo效果

关于AAChartModel 属性说明

  • AAChartModel 主要属性说明
属性名称描述取值范围
title图表主标题任意有效的字符串
subtitle图表副标题任意有效的字符串
chartType图表类型,可以为AAChartType枚举字符串当中指定的任意有效类型.其中有支持柱状图条形图折线图曲线图折线填充图曲线填充图雷达图扇形图气泡图散点图金字塔图漏斗图区域范围图柱形范围图等多种图形.column, .bar, .area, .areaSpline, .line, .spline, .pie, .bubble, .scatter, .pyramid, .funnel, .areaRange, .columnRange
stacking是否将图表每个数据列的值叠加在一起。 默认的值为.none, 即禁用堆叠样式效果.另有常规堆叠样式和百分比堆叠样式可供选择.none, .normal, .percent
symbol图表曲线连接点的样式类型.其可供选择的值有正方形钻石常规三角形倒三角形,默认为混合样式.circle, .square, .diamond, .triangle, .triangleDown
colorsTheme图表显示的颜色主题效果类似此 new String[]{"#fe117c","#ffc069","#06caf4","#7dffc0"} 有效十六进制颜色字符串数组
series图表的数据列AASeriesElement实例对象组成的有效数组,其中每个AASeriesElement都有与之对应的数据、类型、颜色、透明度等具体的值

【通用对话框】 DialogPlus

https://github.com/orhanobut/dialogplus

演示效果

在这里插入图片描述

在这里插入图片描述

使用方法

首先导入依赖

implementation 'com.orhanobut:dialogplus:1.11@aar'

Java类中使用:

基本用法

DialogPlus dialog = DialogPlus.newDialog(this)
  .setAdapter(adapter)//设置适配器,如果内容类型为ListHolder或者GridHolder则是必需项,如果是ViewHolder则不需要。
    //设置Item单击监听器
  .setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(DialogPlus dialog, Object item, View view, int position) {
    }
  })
    //
  .setExpanded(true)  // 启用扩展功能(类似Android L 共享对话框)
  .create();
dialog.show();

Demo

Demo示例

xml文件:

自定义布局文件:activity_test.xml

java类:

Button button1 = findViewById(R.id.dialog_button1);
button1.setOnClickListener((v)-> {
    DialogPlus dialog = DialogPlus.newDialog(this)
            .setContentHolder(new ViewHolder(R.layout.activity_test))
            .setContentBackgroundResource(R.color.purple_200)
            .create();
    dialog.show();
});

Button button2 = findViewById(R.id.dialog_button2);
button2.setOnClickListener((v)-> {
    DialogPlus dialog = DialogPlus.newDialog(this)
            .setContentHolder(new ViewHolder(R.layout.activity_test))
            .setGravity(Gravity.CENTER)
            .setContentBackgroundResource(R.color.purple_200)
            .create();
    dialog.show();
});

Button button3 = findViewById(R.id.dialog_button3);
button3.setOnClickListener((v)-> {
    DialogPlus dialog = DialogPlus.newDialog(this)
            .setContentHolder(new ViewHolder(R.layout.activity_test))
            .setGravity(Gravity.TOP)
            .setContentBackgroundResource(R.color.purple_200)
            .create();
    dialog.show();
});
Demo效果

更多

.setContentHolder(new ListHolder())//可传入三种内容类型:ListHolder,ViewHolder,GridHolder,默认为Listholder,如果要自定义视图则需要用ViewHolder。
.setGravity(Gravity.CENTER)//设置对话框位置,默认为底部。
.setCancelable(true)//定义对话框能否取消。
.setFooter(R.layout.footer)//设置页脚视图
.setHeader(R.layout.header)//设置页眉视图
......
......
......

【引导队列】 MaterialShowcaseView

https://github.com/deano2390/MaterialShowcaseView

演示效果

在这里插入图片描述

使用方法

首先将jitpack仓库添加到根项目的gradle文件中

allprojects {
	repositories {
		jcenter()
		maven { url "https://www.jitpack.io" }
	}
}

注意:新版AS在setting.gradle文件中添加仓库

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://www.jitpack.io' }
    }
}

然后导入依赖

implementation 'com.github.deano2390:MaterialShowcaseView:1.3.7'

Demo

Demo示例

xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"/>

    <com.github.ybq.android.spinkit.SpinKitView
        android:id="@+id/spin_kit"
        style="@style/SpinKitView.Large.Circle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        app:SpinKit_Color="@color/design_default_color_primary" />

    <ProgressBar
        android:layout_marginTop="20dp"
        android:layout_gravity="center"
        android:id="@+id/progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <Button
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        android:id="@+id/button"
        android:layout_width="100dp"
        android:layout_height="50dp"/>
    <TextView
        android:text="TextView"
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <ImageView
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        android:id="@+id/image"
        android:layout_width="100dp"
        android:layout_height="100dp"/>
</LinearLayout>

java代码

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ......
    ......
    // sequence example
    ShowcaseConfig config = new ShowcaseConfig();
    config.setDelay(500); // 每个view展示的间隔时间

    MaterialShowcaseSequence sequence = new MaterialShowcaseSequence(this, SHOWCASE_ID);

    sequence.setConfig(config);

    sequence.addSequenceItem(tv,
            "这是TextView", "明白");

    sequence.addSequenceItem(spinKitView,
            "这是SpinKitView", "明白");

    sequence.addSequenceItem(mButton,
            "这是Button", "明白");

    sequence.addSequenceItem(imageView,
            "这是ImageView", "完成");

    sequence.start();
}
Demo效果

【安全键盘】 SafeKeyboard

https://github.com/SValence/SafeKeyboard

演示效果

在这里插入图片描述

使用方法

项目中新建一个KeyBoard模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4ddOMvu-1671423229984)(assets/image-20221216174123329.png)]

将开源项目中的SafeKeyboardLib下的所有文件导入KeyBoard模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZZvQ6Jk-1671423229985)(assets/image-20221216173930522.png)]

Demo

Demo示例

xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/main_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <EditText
            android:id="@+id/phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="手机号"
            android:inputType="number" />
        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="密码"
            android:inputType="textPassword" />
        <EditText
            android:id="@+id/text1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="text1"
            android:inputType="phone"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/keyboardPlace"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical"
        android:background="#000000"/>

</RelativeLayout>

java代码

    private void initView() {
        phone = findViewById(R.id.phone);
        password = findViewById(R.id.password);
        View root = findViewById(R.id.root);
        View mainRoot = findViewById(R.id.main_root);
        LinearLayout keyboardContainer = findViewById(R.id.keyboardPlace);
        safeKeyboard = new SafeKeyboard(
                getApplicationContext(),
                keyboardContainer,
                root,
                mainRoot
                );
/*        safeKeyboard.putEditText(phone);
        safeKeyboard.putEditText(password);*/
        safeKeyboard.putRandomEdit(phone);
        safeKeyboard.putRandomEdit(password);
    }
Demo效果

更多

支持数字和字母随机

除常规数字、字母、符号键盘外还有一些特殊的键盘样式

支持自定义键盘布局,可通过手动更改源码来实现

【菜单】 BoomMenu

https://github.com/Nightonke/BoomMenu

演示效果

在这里插入图片描述

使用方法

首先导入依赖

  dependencies {
     	implement 'com.nightonke:boommenu:2.1.1'
  }

在xml文件中添加组件

<com.nightonke.boommenu.BoomMenuButton
    android:id="@+id/bmb"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />

然后进行菜单按钮的初始化

bmb.setButtonEnum(ButtonEnum.SimpleCircle);//菜单按钮的类型
bmb.setPiecePlaceEnum(PiecePlaceEnum.DOT_3_1);//菜单按钮的预览样式(未展开时)
bmb.setButtonPlaceEnum(ButtonPlaceEnum.SC_3_3);//菜单按钮展开时如何显示在屏幕上

添加构建器

for (int i = 0; i < bmb.getButtonPlaceEnum().buttonNumber(); i++) {
    bmb.addBuilder(new SimpleCircleButton.Builder()
            .normalImageRes(R.drawable.jellyfish));
} 

Demo

Demo示例

xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".BoomMenu">

    <com.nightonke.boommenu.BoomMenuButton
        android:id="@+id/bmb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <com.nightonke.boommenu.BoomMenuButton
        android:id="@+id/bmb2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:bmb_buttonEnum="ham"
        app:bmb_buttonPlaceEnum="buttonPlace_ham_3"
        app:bmb_piecePlaceEnum="piecePlace_ham_3" />
</LinearLayout>

java代码

public class BoomMenu extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_boom_menu);
        BoomMenuButton bmb = (BoomMenuButton) findViewById(R.id.bmb);
        BoomMenuButton bmb2 = (BoomMenuButton) findViewById(R.id.bmb2);
        bmb.setButtonEnum(ButtonEnum.SimpleCircle);
        bmb.setPiecePlaceEnum(PiecePlaceEnum.DOT_3_1);
        bmb.setButtonPlaceEnum(ButtonPlaceEnum.SC_3_3);
        bmb.addBuilder(new SimpleCircleButton.Builder()
                .normalImageRes(R.mipmap.share_wechat));
        bmb.addBuilder(new SimpleCircleButton.Builder()
                .normalImageRes(R.mipmap.share_wechat_moments));
        bmb.addBuilder(new SimpleCircleButton.Builder()
                .normalImageRes(R.mipmap.share_micro_blog));
        bmb2.addBuilder(new HamButton.Builder()
                .normalImageRes(R.mipmap.share_wechat)
                .normalTextRes(R.string.share_wechat_title)
                .subNormalTextRes(R.string.share_wechat_content)
                .listener(new OnBMClickListener() {
                    @Override
                    public void onBoomButtonClick(int index) {
                        // When the boom-button corresponding this builder is clicked.
                        Toast.makeText(getApplicationContext(), "Clicked " + index + "分享到微信", Toast.LENGTH_SHORT).show();
                    }
                }));
        bmb2.addBuilder(new HamButton.Builder()
                .normalImageRes(R.mipmap.share_wechat_moments)
                .normalTextRes(R.string.share_wechat_circle_title)
                .subNormalTextRes(R.string.share_wechat_circle_content)
                .listener(new OnBMClickListener() {
                    @Override
                    public void onBoomButtonClick(int index) {
                        // When the boom-button corresponding this builder is clicked.
                        Toast.makeText(getApplicationContext(), "Clicked " + index + "分享到朋友圈", Toast.LENGTH_SHORT).show();
                    }
                }));
        bmb2.addBuilder(new HamButton.Builder()
                .normalImageRes(R.mipmap.share_micro_blog)
                .normalTextRes(R.string.share_weibo_title)
                .subNormalTextRes(R.string.share_weibo_content)
                .listener(new OnBMClickListener() {
                    @Override
                    public void onBoomButtonClick(int index) {
                        // When the boom-button corresponding this builder is clicked.
                        Toast.makeText(getApplicationContext(), "Clicked " + index + "分享到微博", Toast.LENGTH_SHORT).show();
                    }
                }));
    }
}
Demo效果

更多

更多使用方法请查看BoomMenu的使用文档

https://github.com/Nightonke/BoomMenu/wiki

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ModernUI(http://mui.codeplex.com/)是一个开源的WPF界面库,利用该界面库,我们可以创建很酷的应用程序。下面是ModernUI官方示例,你可以从官方网站直接下载源码运行,如果是.NET 4.0的话,记得要声明“NET4”预编译变量,否则无法编译通过。 这个界面框架是基于ModernUI来实现的,在该文我将分享所有的源码,并详细描述如何基于ModernUI来构造一个非常通用的、插件化的WPF开发框架。下载源码的同志,希望点击一下推荐。 本文将按照以下四点来介绍: (1)ModernUI简介; (2)构建通用界面框架的思路; (3)基于ModernUI和OSGi.NET的插件化界面框架实现原理及源码分析; (4)其它更有趣的东西~~。 要编写这样的WPF界面,我们需要在一个Window上声明菜单和Tab页面,下图是定义菜单的声明。 此外,每一个Tab风格页面,你也需要手动的为菜单创建这样的界面元素。 直接用这样的方式来使用ModernUI,显然不太适合团队协作性的并行开发,因为在一个团队的协作中,不同的人需要完成不同的功能,实现不同页面,每个人都需要来更改主界面。 我非常希望模块化的开发方法,因为这可以尽可能的复用现有资产,使程序员可以聚焦在自己关注的业务逻辑上,不需要关心UI的使用。下面,我将来描述基于ModernUI实现的一个通用界面框架,这个界面框架允许程序员在自己的业务模块中配置需要显示的界面元素。 通用界面框架实现思路: 我希望能够实现这样的通用界面框架: (1)程序员可以直接实现需要展现业务逻辑的界面,不需要关注如何使用ModernUI; (2)程序员可以通过简单的配置就可以将自己实现的业务逻辑页面显示在主界面中; (3)这个界面框架可以完全复用。 当我看到ModernUI这个界面库时,我希望将应用程序做成模块化,每一个模块能够: (1)通过以下配置能够直接显示二级菜单。 (2)通过以下配置能够直接显示三级菜单。 这样做的好处是,开发插件的时候可以不需要关心界面框架插件;团队在协作开发应用的时候,可以独立开发并不需要修改主界面;团队成员的插件可以随时集成到这个主界面;当主界面无法满足我们的布局时或者用户需求无法满足时,可以直接替换主界面框架而不需要修改任何插件代码。 最终的效果如下,以下界面的几个菜单及点击菜单显示的内容由DemoPlugin插件、DemoPlugin2插件来提供。当插件框架加载更多插件时,界面上会出现更多的菜单;反之,当插件被卸载或者被停止时,则相应的菜单将消失掉。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值