Android——完全自定义 底部弹出支付页面

近期博主在网上随便搜了一番,发现很多实现 底部弹出支付页面的大多数都用的,自定义PopupWindow 去实现的,里面复杂的逻辑看得我不想看,很多对自定义不熟悉伙伴们,看到 自定义 这三个字就有种血脉压制的感觉,别担心,博主以前也是和你们一样的,想弄好自定义还是要自己多多去动手实现去看看关于自定义的书籍,本次博主推荐一个用起来 非常舒服,颠覆传统自定义 的方式来做一个 底部弹出支付页面,想要做出非常友好的底部弹出支付页面,或者说其他华丽的动画弹出任意页面,那这篇文章不可错过哟!

先看效果图:

 这里的显示效果,虽然是从底部弹出的,但弹出的方式是完全自己自定义实现的,不想其他方式实现的有诸多限制,该弹出动画你可以做成 天马行空 都没问题。

本章内容重点使用 GT库里 GT_View 封装类进行实现 自定义弹窗,

其实GT库中提供封装好的类 有很多:

 这些都时候GT库提供封装的类,后续还会不定时添加的,本篇仅仅介绍 GT库中的 View 封装类的使用。

  使用GT库里的,当然需要先依赖好GT库啦:

GitHub - 1079374315/GTContribute to 1079374315/GT development by creating an account on GitHub.https://github.com/1079374315/GT

那咋们来看看,这个效果是怎么天马行空实现的吧,我们先把 样式、资源这些加上

资源:

 round_whitelucency_bg13.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#008E8E8E"/>
    <!-- 设置圆角 -->
    <corners android:radius="1dp"/>
    <!-- 设置边框 -->
    <stroke
        android:width="1dip"
        android:color="#6B000000" />

</shape>

round_whitelucency_bg17.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#FFFFFF"/>
    <!-- 设置圆角 -->
    <corners
        android:topRightRadius="15dp"
        android:topLeftRadius="15dp"
        />
    <!-- 设置边框 -->
    <stroke
        android:width="1dip"
        android:color="#FFFFFF" />

</shape>

样式:

<style name="tv_pass">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:layout_weight">1</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">#000000</item>
        <item name="android:background">#FFFFFF</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textSize">28sp</item>
        <item name="android:layout_marginEnd">2dp</item>
        <item name="android:layout_marginBottom">2dp</item>
    </style>

    <style name="et_pass">
        <item name="android:layout_width">50dp</item>
        <item name="android:layout_height">50dp</item>
        <item name="android:gravity">center</item>
        <item name="android:maxLength">1</item>
        <item name="android:inputType">numberPassword</item>
        <item name="android:layout_marginStart">5dp</item>
        <item name="android:background">@drawable/round_whitelucency_bg13</item>
        <item name="android:textCursorDrawable">@drawable/color_cursor</item>
    </style>

美化的样式资源图片就这些了,接下来附上

自定义 xml 布局:

<?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:id="@+id/cl_filtrate"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:ignore="RtlSymmetry,LabelFor,NestedWeights,HardcodedText">

    <View
        android:id="@+id/view_bg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#70000000" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/ll_bottom1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@drawable/round_whitelucency_bg17"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="h,1:1">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="15dp"
            android:text="请输入二级密码"
            android:textColor="#202020"
            android:textStyle="bold"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <View
            android:id="@+id/view_line"
            android:layout_width="match_parent"
            android:layout_height="2dp"
            android:layout_marginTop="15dp"
            android:background="#EEEEEE"
            app:layout_constraintTop_toBottomOf="@+id/tv_title" />

        <!-- 密码框 -->
        <LinearLayout
            android:id="@+id/ll_pass"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15dp"
            android:gravity="center"
            android:orientation="horizontal"
            app:layout_constraintTop_toBottomOf="@+id/view_line">

            <EditText
                android:id="@+id/et_1"
                style="@style/et_pass"
                android:focusable="true" />

            <EditText
                android:id="@+id/et_2"
                style="@style/et_pass" />

            <EditText
                android:id="@+id/et_3"
                style="@style/et_pass" />

            <EditText
                android:id="@+id/et_4"
                style="@style/et_pass" />

            <EditText
                android:id="@+id/et_5"
                style="@style/et_pass" />

            <EditText
                android:id="@+id/et_6"
                style="@style/et_pass" />
        </LinearLayout>

        <TextView
            android:id="@+id/tv_pass"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:padding="10dp"
            android:text="忘记密码?请戳我"
            android:textColor="#202020"
            android:textSize="12sp"
            android:textStyle="bold"
            app:layout_constraintEnd_toEndOf="@+id/ll_pass"
            app:layout_constraintStart_toStartOf="@+id/ll_pass"
            app:layout_constraintTop_toBottomOf="@+id/ll_pass" />

        <View
            android:id="@+id/view_line2"
            android:layout_width="match_parent"
            android:layout_height="3dp"
            android:layout_marginTop="5dp"
            android:background="#EEEEEE"
            app:layout_constraintTop_toBottomOf="@+id/tv_pass" />

        <LinearLayout
            android:id="@+id/ll_bottom"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:background="#EEEEEE"
            android:orientation="vertical"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/view_line2">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    style="@style/tv_pass"
                    android:text="1" />

                <TextView
                    style="@style/tv_pass"
                    android:text="2" />

                <TextView
                    style="@style/tv_pass"
                    android:text="3" />

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    style="@style/tv_pass"
                    android:text="4" />

                <TextView
                    style="@style/tv_pass"
                    android:text="5" />

                <TextView
                    style="@style/tv_pass"
                    android:text="6" />

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    style="@style/tv_pass"
                    android:text="7" />

                <TextView
                    style="@style/tv_pass"
                    android:text="8" />

                <TextView
                    style="@style/tv_pass"
                    android:text="9" />

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    style="@style/tv_pass"
                    android:background="#EEEEEE" />

                <TextView
                    style="@style/tv_pass"
                    android:text="0" />

                <LinearLayout
                    android:id="@+id/ll_delete"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:gravity="center">

                    <ImageView
                        android:layout_width="40dp"
                        android:layout_height="40dp"
                        android:src="@drawable/ic_delete" />
                </LinearLayout>

            </LinearLayout>

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

布局效果图:

咋们再来看看自定义底部弹窗 核心代码

//加载自定义xml布局
@GT.Annotations.GT_AnnotationView(R.layout.view_pay_pass)
public class PayPassView extends GT.GT_View.AnnotationView {

    /**
     * 必须写的
     * @param context
     * @param viewGroup 装入支付弹窗容器(后面会介绍使用的)
     */
    public PayPassView(Context context, ViewGroup viewGroup) {
        super(context, viewGroup);
    }

    private View view_bg;
    private View ll_bottom1;

    //需要重写的初始化方法
    @Override
    protected void initView(View view) {
        super.initView(view);

        //获取组件
        view_bg = findViewById(R.id.view_bg);
        ll_bottom1 = findViewById(R.id.ll_bottom1);

        //单击事件
        ll_bottom1.setOnClickListener(null);//消耗掉点击密码框的单击事件
        view_bg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                hide();//单击上部分 半透明黑背景 进行隐藏动画操作
            }
        });
    }

    //显示动画
    public void show() {
        view_bg.setVisibility(View.VISIBLE);
        ll_bottom1.setVisibility(View.VISIBLE);
    }

    //隐藏动画
    private void hide() {
        view_bg.setVisibility(View.GONE);
        ll_bottom1.setVisibility(View.GONE);
    }

}

看到上面的实现方法,够简单吧,唯一有疑惑的应该就只有 加载自定义xml布局 了吧,这个加载自定义xml布局是 GT库非常基础的一个实现 加载布局 的方式,上面展示的是 GT库 加载布局 版本的第二版,一般博主都不用的淘汰版,不是不好用,还是有更方便的版本,博主都用 GT库 加载布局 第四版想要了解更多GT库加载布局的方式我放下面了:

Android——GT-DataBinding(彻底解放 findViewById)_PlayfulKing的博客-CSDN博客熟悉 GT 库的朋友们有福了,我们一起来看看组件获取组件的变化第一版:Android 原生版本public class MainActivity extends AppCompatActivity { private TextView tv;//定义组件 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);https://blog.csdn.net/qq_39799899/article/details/121270536?spm=1001.2014.3001.5501家人们,用第四版是真的爽,等后面文章会贴上第四版进行与第二版对比的

接下来咋们再来看看 调用 支付弹窗页面的布局:

activity_main.xml

<?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"
    >

    <TextView
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始支付"
        android:background="@drawable/bg_black_et"
        app:layout_constraintVertical_bias="0.28"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:paddingStart="15dp"
        android:paddingEnd="15dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <RelativeLayout
        android:id="@+id/rl"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:layout_editor_absoluteX="16dp"
        tools:layout_editor_absoluteY="231dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

里面的 bg_black_et.xml 是GT库 提供的,可以直接使用

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private PayPassView payPassView;
    private RelativeLayout rl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        rl = findViewById(R.id.rl);//获取组件容器

        //构建 弹窗支付页面 并将构建好的支付页面 添加到 容器 rl 中
        payPassView = new PayPassView(this,rl);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                payPassView.show();//显示支付页面
            }
        });

    }

}

这个简单吧,其实就是将我们创建好的 支付弹窗添加到 容器中。写到这里就可以直接运行看看效果了:

当前代码运行 效果图:

 效果都出来了吧,看到这里是不是感觉,添加一个支付页面很简单呢,这就是完全的自定义布局页面,这些家人们不用再对自定义有所顾忌了吧,

GT库为简单而生

做到这,效果是出来了,但是不是看到动画很僵硬,而且点击数字和回退按钮都没反应,那是肯定的,咱们的核心代码里啥逻辑都没有,肯定没啥反应不,接下来,博主就来接着完善剩余的逻辑处理,先把这个第二版 加载布局给换成 第四版 的,第四版需要加上 gt-DataBinding 功能,

 供复制:

//注册 gt-DataBinding 功能
annotationProcessor 'com.github.1079374315:GSLS_Tool:v1.4.2.4'

将红色框框里的复制粘贴注册好就可以使用第四版 加载布局了

添加注册好后,咋们来将核心代码改改:

继承 PayPassViewBinding 类时 会爆红,因为这个类时编译后才会自动产生的 

核心代码:

//使用 GT_DataBinding 功能
@GT_DataBinding(setLayout = "view_pay_pass", setBindingType = GT_DataBinding.View)
@GT.Annotations.GT_AnnotationView(R.layout.view_pay_pass)//加载自定义xml布局
public class PayPassView extends PayPassViewBinding {

    public PayPassView(Context context, ViewGroup viewGroup) {
        super(context, viewGroup);
    }

    @Override
    protected void initView(View view) {
        super.initView(view);
        ll_bottom1.setOnClickListener(null);//消耗单击密码框事件
        hide();//装入容器后直接隐藏
    }

    //设置单击事件
    @GT.Annotations.GT_Click({R.id.view_bg, R.id.tv_pass})
    public void onCLick(View view) {
        switch (view.getId()) {
            case R.id.tv_pass://忘记密码
                GT.toast(context, "单击了忘记密码");//吐司
                break;
            case R.id.view_bg:
                hide();//单击上部分 半透明黑背景 进行隐藏动画操作
                break;
        }
    }

    //显示动画
    public void show() {
        view_bg.setVisibility(View.VISIBLE);
        ll_bottom1.setVisibility(View.VISIBLE);
    }

    //隐藏动画
    private void hide() {
        view_bg.setVisibility(View.GONE);
        ll_bottom1.setVisibility(View.GONE);
    }

}

原来的核心代码:

 这样一对比是不是感觉 比之前的代码简洁了很多

你再运行看看,和原来的效果是一样的,接下来咋们后续都是以第四版来添加后续的逻辑处理了

想要添加动画,那博主第一想的就是 GT库内的 强大的动画库 操作简单,注释明了

核心代码修改:

//使用 GT_DataBinding 功能
@GT_DataBinding(setLayout = "view_pay_pass", setBindingType = GT_DataBinding.View)
@GT.Annotations.GT_AnnotationView(R.layout.view_pay_pass)//加载自定义xml布局
public class PayPassView extends PayPassViewBinding {

    public PayPassView(Context context, ViewGroup viewGroup) {
        super(context, viewGroup);
    }

    @Override
    protected void initView(View view) {
        super.initView(view);
        ll_bottom1.setOnClickListener(null);//消耗单击密码框事件
        hide();//装入容器后直接隐藏
    }

    //设置单击事件
    @GT.Annotations.GT_Click({R.id.view_bg, R.id.tv_pass})
    public void onCLick(View view) {
        switch (view.getId()) {
            case R.id.tv_pass://忘记密码
                GT.toast(context, "单击了忘记密码");//吐司
                break;
            case R.id.view_bg:
                hide();//单击上部分 半透明黑背景 进行隐藏动画操作
                break;
        }
    }

    //构建GT动画库
    private static final GT.GT_Animation animation = new GT.GT_Animation();
    //定义 必要的计算参数值
    private int height;

    //显示动画
    public void show() {
        //获取容器的这个View的宽高,好设置动画的动态数据
        height = view_bg.getHeight();

        //使用GT库 移动 Y轴(上下) 动画,来给 组件 ll_bottom1 设置移动
        animation.translateY_T(height, 0, 300, 0, false, ll_bottom1);
        view_bg.setVisibility(View.VISIBLE);//显示半透明黑色背景
    }

    //隐藏动画
    private void hide() {
        //获取容器的这个View的宽高,好设置动画的动态数据
        height = view_bg.getHeight();

        if (height == 0) {//在刚开始创建的时候获取的高会为0,所以直接隐藏支付页
            animation.translateY_T(0, 3000, 1, 0, false, ll_bottom1);
        }else{//后续就更具动态高度进行动画展示
            animation.translateY_T(0, height, 300, 0, false, ll_bottom1);
        }
        view_bg.setVisibility(View.INVISIBLE);//隐藏半透明黑色背景
    }

}

效果图:

这时运行显示支付页面就有 底部弹出效果了,使用 GT 动画库 里的 Y轴移动 动画就可以很好的达到底部弹出的效果,而且使用非常简单, GT库的参数不会的小伙伴们可以,自己去对着 形参注释 一个一个参数试,注释也很人性:

 接下来,博主就直接粘贴所有逻辑处理核心代码了,不再做过多的解释

最终核心代码:

@GT_DataBinding(setLayout = "view_pay_pass", setBindingType = GT_DataBinding.View)
@GT.Annotations.GT_AnnotationView(R.layout.view_pay_pass)
public class PayPassView extends PayPassViewBinding {

    private int height;
    private int width;
    private List<EditText> editTextList;
    private int index;//索引

    private static GT.GT_Animation animation = new GT.GT_Animation();

    public PayPassView(Context context, ViewGroup viewGroup) {
        super(context, viewGroup);
    }

    @Override
    protected void initView(View view) {
        super.initView(view);
        hide();
        ll_bottom1.setOnClickListener(v -> {
        });
        editTextList = new ArrayList<>();
    }


    @Override
    public void loadData(View view) {
        super.loadData(view);

        //设置第一个密码框光标
        GT.Thread.getInstance(0).execute(() -> {
            GT.Thread.sleep(300);
            index = 0;
            GT.Thread.runAndroid(() -> et_1.requestFocus());
        });

        //设置每个单独密码的事件
        for (int i = 0; i < ll_pass.getChildCount(); i++) {
            EditText editText = (EditText) ll_pass.getChildAt(i);
            editText.setTag(i);//设置吗每个 EditText 索引
            editTextList.add(editText);
            int finalI = i;
            editText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                }

                @Override
                public void afterTextChanged(Editable s) {
                    String s1 = editText.getText().toString();
                    editText.setSelection(s1.length());
                    if (s1.length() == 0) {//删除的时候
                        index = Integer.parseInt(editText.getTag().toString());
                        if (index - 1 >= 0) {
                            EditText editText1 = editTextList.get(index - 1);
                            editText1.requestFocus();
                        }
                        if (index > 0)
                            index--;
                    } else if (s1.length() == 1) {//输入的时候
                        if (finalI + 1 <= editTextList.size() - 1) {
                            EditText editText1 = editTextList.get(finalI + 1);
                            editText1.requestFocus();
                            index = Integer.parseInt(editText1.getTag().toString());
                        }
                    }
                    transactionPasswordAuthentication();

                }

                //验证交易密码是否全部输入
                private void transactionPasswordAuthentication() {
                    boolean isOk = true;
                    String apppwd = "";
                    for (int i = 0; i < editTextList.size(); i++) {
                        EditText editText1 = editTextList.get(i);
                        String s = editText1.getText().toString();
                        if (s.length() == 0) {
                            isOk = false;
                        }
                        apppwd += s;
                    }
                    if (!isOk) return;
//                    AppUtils.toast("正在支付中...");

                    for (int i = 0; i < editTextList.size(); i++) {
                        EditText editText1 = editTextList.get(i);
                        editText1.setText("");
                    }

                    //这里的支付密码可以通过接口返回数据 或者 GT.EventBus 传递消息出去
                    GT.logt("支付密码:" + apppwd);
                    GT.toast(context, "支付密码:" + apppwd);
                    hide();

                }
            });
        }

        //单击密码设置
        for (int i = 0; i < ll_bottom.getChildCount(); i++) {
            View childAt = ll_bottom.getChildAt(i);
            if (childAt instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) childAt;
                for (int j = 0; j < vg.getChildCount(); j++) {
                    View childAt1 = vg.getChildAt(j);

                    //输入密码
                    if (childAt1 instanceof TextView) {
                        TextView tv = (TextView) childAt1;
                        tv.setOnClickListener(v -> {
                            EditText editText = editTextList.get(index);
                            editText.setText(tv.getText().toString());
                        });
                    }

                    //删除单个密码框的操作
                    if (childAt1 instanceof LinearLayout) {
                        childAt1.setOnClickListener(v -> {
                            EditText editText = editTextList.get(index);
                            editText.setText("");
                        });
                    }

                }
            }
        }

    }

    //单击事件
    @GT.Annotations.GT_Click({R.id.view_bg, R.id.tv_pass})
    public void onCLick(View view) {
        switch (view.getId()) {
            case R.id.tv_pass://忘记密码
                GT.toast(context, "单击了忘记密码");
                break;
            case R.id.view_bg:
                hide();
                break;

        }
    }

    //显示动画
    public void show() {

        //设置第一个密码框光标
        GT.Thread.getInstance(0).execute(() -> {
            GT.Thread.sleep(300);
            index = 0;
            GT.Thread.runAndroid(() -> {
                if (et_1 != null) et_1.requestFocus();
            });
        });

        height = view_bg.getHeight();
        width = view_bg.getWidth();

        animation.translateY_T(height, 0, 300, 0, false, ll_bottom1);
        view_bg.setVisibility(View.VISIBLE);
        GT.Thread.getInstance(0).execute(() -> {
            for (float a = 0; a < 1; ) {
                if (view_bg == null) break;
                a += 0.01;
                GT.Thread.sleep(1);
                float finalA = a;
                GT.Thread.runAndroid(() -> {
                    if (view_bg != null) {
                        view_bg.setAlpha(finalA);
                    }
                });
            }
        });
    }

    //隐藏动画
    private void hide() {

        //清空密码框剩余的数据
        GT.Thread.getInstance(0).execute(() -> GT.Thread.runAndroid(() -> {
            for (int i = 0; i < editTextList.size(); i++) {
                EditText editText1 = editTextList.get(i);
                if (editText1 == null) return;
                editText1.setText("");
            }
        }));

        height = view_bg.getHeight();
        width = view_bg.getWidth();

        if (height == 0) {
            animation.translateY_T(0, 3000, 1, 0, false, ll_bottom1);
            view_bg.setAlpha(0);
            view_bg.setVisibility(View.INVISIBLE);
        } else {
            animation.translateY_T(0, height, 300, 0, false, ll_bottom1);
            GT.Thread.getInstance(0).execute(() -> {
                for (float a = 1; a > 0; ) {
                    a -= 0.01;
                    GT.Thread.sleep(1);
                    float finalA = a;
                    GT.Thread.runAndroid(() -> {
                        if (view_bg != null) {
                            view_bg.setAlpha(finalA);
                            view_bg.setVisibility(View.INVISIBLE);
                        }
                    });

                }
            });
        }

    }

}

本篇重点再介绍 GT_View 封装类,当你学会 GT_View 封装类,那自定义其他弹窗岂不是手到擒来,再加上 熟练使用 GT库动画库,那 左弹、右弹、上跳、下跳、啥天马行空动画不能做,GT库里好包括 Z 轴动画,将这些动画组合起来可以直接产生 3D 效果哦。

本章GT_View + GT动画库,实现的 自定义 底部支付弹窗

点个关注点个赞呗(〃'▽'〃)   关注博主最新发布库:GitHub - 1079374315/GT

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PlayfulKing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值