Android辅助功能实现自动抢红包

###一、描述

最近看到同事有用抢红包的软件,就想看看抢红包的具体实现是如何的,所以了解了一下,有用辅助功能实现的,所以在下面的示例中会展示一个抢红包的小Demo,附带源码抢红包源码

###二、效果图

###三、AccessibilityService使用

  1. 创建辅助服务类,继承AccessibilityService,实现两个接口,接收系统的事件
public class MyService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        
    }

    @Override
    public void onInterrupt() {
        
    }
}

复制代码

2. 辅助服务的配置文件,配置事件,在 res/xml下创建accessibility_service_info.xml
//具体属性的说明在第5点有说明
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="100"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:packageNames="top.cokernut.sample"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"  />
复制代码

3. 注册Service辅助服务,并且为Service附加上第二步创建的xml,看清除下面的一些属性,必须要加,如果有的没加的话是没效果的
<service
            android:name=".MyService"
            android:label="辅助功能"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>

            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_info" />
        </service>
复制代码

4 清单文件中添加权限 ``` ```
  1. 辅助服务配置文件xml属性说明:
//是否可以检索整个层级下的内容
android:canRetrieveWindowContent="true"级下的信息

//事件通知触发点,比如窗口打开,滑动,焦点变化,长按等。
android:accessibilityEventTypes="typeAllMask"
#TYPES_ALL_MASK:所有类型
#TYPE_VIEW_CLICKED :单击
#TYPE_VIEW_LONG_CLICKED :长按
#TYPE_VIEW_SELECTED :选中
#TYPE_VIEW_FOCUSED :获取焦点
#TYPE_VIEW_TEXT_CHANGED :文字改变
#TYPE_WINDOW_STATE_CHANGED :窗口状态改变

//表示反馈方式,比如是语音播放,还是震动
android:accessibilityFeedbackType="feedbackGeneric"

//接受事件的时间间隔,通常将其设置为100即可.
android:notificationTimeout="100"

//表示该服务是用来单独监听哪个应用的产生的事件,其他的都会过滤,如果不填就是对所有的应用进行监听,填入包名即可。
android:packageNames="top.cokernut.sample"

//在代码中我们就可以通过node节点来getViewIdResourceName()获取对应的节点的id
android:accessibilityFlags="flagDefault"


复制代码

提供一个AccessibilityService的基类,集成了一些常用方法:

public class BaseAccessibilityService extends AccessibilityService {

    private AccessibilityManager mAccessibilityManager;
    private Context mContext;
    private static BaseAccessibilityService mInstance;

    public void init(Context context) {
        mContext = context.getApplicationContext();
        mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
    }

    public static BaseAccessibilityService getInstance() {
        if (mInstance == null) {
            mInstance = new BaseAccessibilityService();
        }
        return mInstance;
    }

    /**
     * Check当前辅助服务是否启用
     *
     * @param serviceName serviceName
     * @return 是否启用
     */
    private boolean checkAccessibilityEnabled(String serviceName) {
        List<AccessibilityServiceInfo> accessibilityServices =
                mAccessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
        for (AccessibilityServiceInfo info : accessibilityServices) {
            if (info.getId().equals(serviceName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 前往开启辅助服务界面
     */
    public void goAccess() {
        Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(intent);
    }

    /**
     * 模拟点击事件
     *
     * @param nodeInfo nodeInfo
     */
    public void performViewClick(AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo == null) {
            return;
        }
        while (nodeInfo != null) {
            if (nodeInfo.isClickable()) {
                nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                break;
            }
            nodeInfo = nodeInfo.getParent();
        }
    }

    /**
     * 模拟返回操作
     */
    public void performBackClick() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        performGlobalAction(GLOBAL_ACTION_BACK);
    }

    /**
     * 模拟下滑操作
     */
    public void performScrollBackward() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
    }

    /**
     * 模拟上滑操作
     */
    public void performScrollForward() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
    }

    /**
     * 查找对应文本的View
     *
     * @param text text
     * @return View
     */
    public AccessibilityNodeInfo findViewByText(String text) {
        return findViewByText(text, false);
    }

    /**
     * 查找对应文本的View
     *
     * @param text      text
     * @param clickable 该View是否可以点击
     * @return View
     */
    public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {
        AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
        if (accessibilityNodeInfo == null) {
            return null;
        }
        List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
        if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
                if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
                    return nodeInfo;
                }
            }
        }
        return null;
    }

    /**
     * 查找对应ID的View
     *
     * @param id id
     * @return View
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    public AccessibilityNodeInfo findViewByID(String id) {
        AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
        if (accessibilityNodeInfo == null) {
            return null;
        }
        List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
        if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
                if (nodeInfo != null) {
                    return nodeInfo;
                }
            }
        }
        return null;
    }

    public void clickTextViewByText(String text) {
        AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
        if (accessibilityNodeInfo == null) {
            return;
        }
        List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
        if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
                if (nodeInfo != null) {
                    performViewClick(nodeInfo);
                    break;
                }
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void clickTextViewByID(String id) {
        AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
        if (accessibilityNodeInfo == null) {
            return;
        }
        List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
        if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
                if (nodeInfo != null) {
                    performViewClick(nodeInfo);
                    break;
                }
            }
        }
    }

    /**
     * 模拟输入
     *
     * @param nodeInfo nodeInfo
     * @param text     text
     */
    public void inputText(AccessibilityNodeInfo nodeInfo, String text) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Bundle arguments = new Bundle();
            arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
            nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clip = ClipData.newPlainText("label", text);
            clipboard.setPrimaryClip(clip);
            nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
            nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
        }
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    }

    @Override
    public void onInterrupt() {
    }
}
复制代码

###四、QQ抢红包

(一)抢红包流程:

  1. 通知栏收到QQ的消息,发现是QQ红包,模拟点击消息进入聊天页面
  2. 检索页面上的所有元素,发现有包含“点击拆开”的字眼,就模拟点击打开红包窗口
  3. 一两秒后执行Back操作,关闭红包窗口。
  4. 继续等待消息来到。

(二)实现功能:

  1. 锁屏抢红包(不可以有密码或者图案之类的锁屏)
  2. 口令红包,自动输入口令并且发送
  3. 抢完红包后,自动回复感谢语,可在红包设置里自行设置内容
  4. 其他的功能就没继续往下做了,知道方法,其他都可能慢慢研究出来。

(三)抢红包辅助功能类,注释都写好了,很好理解,类中有用到QQConstant类,在第四点贴出了代码

/**
 * 描述:QQ抢红包服务
 * 作者:卜俊文
 * 邮箱:344176791@qq.com
 * 日期:2017/11/6 上午9:25
 */
public class EnvelopeService extends BaseAccessibilityService {

    //锁屏、解锁相关
    private KeyguardManager.KeyguardLock kl;

    //唤醒屏幕相关
    private PowerManager.WakeLock wl = null;

    private long delayTime = 0;//延迟抢的时间

    /**
     * 描述:所有事件响应的时候会回调
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/6 上午9:26
     */
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

        //验证抢红包的开关
        if (!invalidEnable()) {
            return;
        }

        //事件类型
        int eventType = event.getEventType();

        //获取包名
        CharSequence packageName = event.getPackageName();
        if (TextUtils.isEmpty(packageName)) {
            return;
        }

        switch (eventType) {

            //状态栏变化
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:

                if (QQConstant.QQ_PACKAGE_NAME.equals(packageName)) {
                    //处理状态栏上QQ的消息,如果是红包就跳转过去
                    progressQQStatusBar(event);
                }
                break;

            //窗口切换的时候回调
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                if (QQConstant.QQ_PACKAGE_NAME.equals(packageName)) {
                    //处理正在QQ聊天窗口页面,有其他群或者人有新的红包提醒,跳转过去。
                    progressNewMessage(event);
                    //处理聊天页面的红包
                    progressQQChat(event);
                }

                break;
        }


    }

    /**
     * 描述:处理新消息
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/3 下午11:21
     */
    private void progressNewMessage(AccessibilityEvent event) {

        if (event == null) {
            return;
        }
        AccessibilityNodeInfo source = event.getSource();
        if (source == null) {
            return;
        }
        //根据event的source里的text,来判断这个消息是否包含[QQ红包]的字眼,有的话就跳转过去
        CharSequence text = source.getText();
        if (!TextUtils.isEmpty(text) && text.toString().contains(QQConstant.QQ_ENVELOPE_KEYWORD)) {
            performViewClick(source);
        }
    }

    /**
     * 描述:验证抢红包是否开启
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/3 下午4:57
     */
    private boolean invalidEnable() {
        return SettingConfig.getInstance().getReEnable();
    }


    /**
     * 描述:处理QQ状态栏
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/1 下午1:49
     */
    public void progressQQStatusBar(AccessibilityEvent event) {
        List<CharSequence> text = event.getText();
        //开始检索界面上是否有QQ红包的文本,并且他是通知栏的信息
        if (text != null && text.size() > 0) {
            for (CharSequence charSequence : text) {
                if (charSequence.toString().contains(QQConstant.QQ_ENVELOPE_KEYWORD)) {
                    //说明存在红包弹窗,马上进去
                    if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
                        Notification notification = (Notification) event.getParcelableData();
                        if (notification == null) {
                            return;
                        }
                        PendingIntent pendingIntent = notification.contentIntent;
                        if (pendingIntent == null) {
                            return;
                        }
                        try {
                            //要跳转之前,先进行解锁屏幕,然后再跳转,有可能你现在屏幕是锁屏状态,先进行解锁,然后打开页面,有密码的可能就不行了
                            wakeUpAndUnlock(MyApp.context);
                            //跳转
                            pendingIntent.send();
                        } catch (PendingIntent.CanceledException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 描述:处理QQ聊天红包
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/1 下午1:56
     */
    public void progressQQChat(AccessibilityEvent event) {

        if (TextUtils.isEmpty(event.getClassName())) {
            return;
            //如果当前页面是聊天页面或者当前的描述信息是"返回消息界面",就肯定是对话页面
        }

        //验证当前事件是否符合查询页面上的红包
        if (!invalidEnvelopeUi(event)) {
            return;
        }

        //延迟点击红包,防止被检测到开了抢红包,不过感觉还是感觉会被检测到,应该有的效果吧...
        try {
            Thread.sleep(delayTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //普通红包,检索点击拆开的字眼。
        List<AccessibilityNodeInfo> envelope = findViewListByText(QQConstant.QQ_CLICK_TAKE_APART, false);
        //处理普通红包
        progressNormal(envelope);

        //口令红包,检索口令红包的字眼。
        List<AccessibilityNodeInfo> passwordList = findViewListByText(QQConstant.QQ_CLICK_PASSWORD_DIALOG, false);
        //处理口令红包
        progressPassword(passwordList);
    }


    /**
     * 描述:验证是否现在是在聊天页面,可以进行抢红包处理
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/3 上午11:52
     *
     * @param event
     */

    public boolean invalidEnvelopeUi(AccessibilityEvent event) {

        //判断类名是否是聊天页面
        if (!QQConstant.QQ_IM_CHAT_ACTIVITY.equals(event.getClassName().toString())) {
            return true;
        }

        //判断页面中的元素是否有点击拆开的文本,有就返回可以进行查询了
        int recordCount = event.getRecordCount();
        if (recordCount > 0) {
            for (int i = 0; i < recordCount; i++) {
                AccessibilityRecord record = event.getRecord(i);
                if (record == null) {
                    break;
                }
                List<CharSequence> text = record.getText();
                if (text != null && text.size() > 0 && text.contains(QQConstant.QQ_CLICK_TAKE_APART)) {
                    //如果文本中有点击拆开的字眼,就返回可以进行查询了
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 回到系统桌面
     */
    private void back2Home(int time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Intent home = new Intent(Intent.ACTION_MAIN);
        home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        home.addCategory(Intent.CATEGORY_HOME);
        startActivity(home);
    }

    /**
     * 描述:处理普通红包
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/1 下午5:02
     */
    public void progressNormal(List<AccessibilityNodeInfo> passwordList) {
        if (passwordList != null && passwordList.size() > 0) {
            for (AccessibilityNodeInfo accessibilityNodeInfo : passwordList) {
                if (accessibilityNodeInfo != null && !TextUtils.isEmpty(accessibilityNodeInfo.getText()) && QQConstant.QQ_CLICK_TAKE_APART.equals(accessibilityNodeInfo.getText().toString())) {

                    //点击拆开红包
                    performViewClick(accessibilityNodeInfo);

                    //回复感谢信息,根据配置文件中配置的回复信息回复
                    String reReplyMessage = SettingConfig.getInstance().getReReplyMessage();
                    if (!TextUtils.isEmpty(reReplyMessage)) {
                        replyMessage(reReplyMessage);
                    }
                }
            }
            //最后延迟事件触发返回事件,关闭红包页面
            performBackClick(1200);
        }
    }

    /**
     * 描述:处理口令红包
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/1 下午4:58
     *
     * @param passwordList
     */
    public void progressPassword(List<AccessibilityNodeInfo> passwordList) {
        if (passwordList != null && passwordList.size() > 0) {
            for (AccessibilityNodeInfo accessibilityNodeInfo : passwordList) {
                if (accessibilityNodeInfo != null && !TextUtils.isEmpty(accessibilityNodeInfo.getText()) && QQConstant.QQ_CLICK_PASSWORD_DIALOG.equals(accessibilityNodeInfo.getText().toString())) {
                    //如果口令红包存在,就在输入框中进行输入,然后发送
                    AccessibilityNodeInfo parent = accessibilityNodeInfo.getParent();
                    if (parent != null) {
                        CharSequence contentDescription = parent.getContentDescription();
                        if (!TextUtils.isEmpty(contentDescription)) {
                            //1. 获取口令
                            String key = (String) contentDescription;
                            if (key.contains(",") && key.contains("口令:")) {
                                key = key.substring(key.indexOf("口令:") + 3, key.lastIndexOf(","));
                            }
                            Log.e("口令", key);

                            //2. 填写口令到编辑框上然后进行发送
                            replyMessage(key);

                            //返回,关闭红包页面
                            performBackClick(1200);
                        }
                    }
                }
            }
        }
    }


    /**
     * 唤醒屏幕并解锁权限
     * <uses-permission android:name="android.permission.WAKE_LOCK" />
     */
    @SuppressLint("Wakelock")
    @SuppressWarnings("deprecation")
    public void wakeUpAndUnlock(Context context) {
        // 点亮屏幕
        wl.acquire();
        // 释放
        wl.release();
        // 解锁
        kl.disableKeyguard();
    }

    /**
     * 描述:回复消息,无延迟
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/3 下午5:10
     */
    public void replyMessage(String key) {
        replyMessage(key, 0);
    }

    /**
     * 描述:回复消息
     * 作者:卜俊文
     * 邮箱:344176791@qq.com
     * 日期:2017/11/3 下午5:10
     */
    public void replyMessage(String key, int time) {

        //延迟
        if (time > 0) {
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //获取QQ聊天页面输入框
        AccessibilityNodeInfo chat_edit = findViewByID(QQConstant.QQ_CHAT_MESSAGE_INPUT);
        if (chat_edit != null) {

            //把口令粘贴到输入框中
            pastaText(chat_edit, MyApp.context, key);

            //获取QQ聊天页面发送消息按钮
            AccessibilityNodeInfo sendMessage = findViewByID(QQConstant.QQ_CHAT_MESSAGE_SEND);

            //然后就按下发送按钮
            if (sendMessage != null && Button.class.getName().equals(sendMessage.getClassName())) {
                performViewClick(sendMessage);
            }
        }

    }

    @Override
    public void onInterrupt() {
    }


    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();

        // 获取电源管理器对象
        PowerManager pm = (PowerManager) MyApp.context
                .getSystemService(Context.POWER_SERVICE);
        // 获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
        wl = pm.newWakeLock(
                PowerManager.ACQUIRE_CAUSES_WAKEUP
                        | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");

        KeyguardManager km = (KeyguardManager) MyApp.context.getSystemService(Context.KEYGUARD_SERVICE);
        kl = km.newKeyguardLock("unLock");

        //初始化屏幕的监听
        ScreenListener screenListener = new ScreenListener(MyApp.context);
        screenListener.begin(new ScreenListener.ScreenStateListener() {
            @Override
            public void onScreenOn() {
                Log.e("ScreenListener", "屏幕打开了");
            }

            @Override
            public void onScreenOff() {
                //在屏幕关闭的时候,进行锁屏,不执行的话,锁屏就失效了,因为要实现锁屏状态下也可以进行抢红包。
                Log.e("ScreenListener", "屏幕关闭了");
                if (kl != null) {
                    kl.disableKeyguard();
                    kl.reenableKeyguard();
                }
            }

            @Override
            public void onUserPresent() {
                Log.e("ScreenListener", "解锁了");
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
复制代码

(四)QQ辅助服务里有用到的常量

public class QQConstant {

    //QQ的应用包名
    public static final String QQ_PACKAGE_NAME = "com.tencent.mobileqq";

    //状态栏红包关键字
    public static final String QQ_ENVELOPE_KEYWORD = "[QQ红包]";

    //QQ聊天页面
    public static final String QQ_IM_CHAT_ACTIVITY = "com.tencent.mobileqq.activity.SplashActivity";

    //点击拆开
    public static final String QQ_CLICK_TAKE_APART = "点击拆开";

    //口令红包
    public static final String QQ_CLICK_PASSWORD_DIALOG = "口令红包";

    //聊天页面,输入框ID
    public static final String QQ_CHAT_MESSAGE_INPUT = "com.tencent.mobileqq:id/input";

    //聊天页面,发送按钮
    public static final String QQ_CHAT_MESSAGE_SEND = "com.tencent.mobileqq:id/fun_btn";

}
复制代码

###五、红包问题

  1. 用的时候偶尔会被QQ检测到用了红包插件,可能是因为抢的速度太快,导致数据不符合正常的点击时间,我有加入一个延迟时间,不知道有没有效果,如果有知道的也可以留言,谢谢。

  2. 在QQ的主页面上,收到消息的时候通知栏是不会通知的,所以这里不能进行解析通知栏跳转聊天页面,没有找到什么元素可以告诉我怎么进入红包的聊天页面,如果有知道的可以留言,谢谢。

  3. 这种辅助服务的方式抢红包,进入聊天页面后,他检索字段只会检索当前页面可视的元素,某些红包要是在聊天记录上面看不见的,需要滑动上去才可以触发解析红包,不过一般不会一次性10个红包都发出来吧,嘿嘿。


###六、总结

学习制作了这个项目,也了解了辅助功能的使用,感觉这个还是可以做很多东西的,上面已经贴出了核心代码,要源码的可以点击。抢红包源码

欢迎关注我的微信公众号,分享更多技术文章。

转载于:https://juejin.im/post/5b3ac527f265da62c70973dc

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值