如何让你的APP更智能|青训营笔记(二)

简介: 随着当下的社会发展,我们的手机屏幕越来越大。我们的单手难以覆盖整个手机,所以当我们想要单手去点击屏幕另一侧的地方的时,就会感到较为困难。这时候我们就会想,这个按钮要是更靠近我们就好了。

工具类

该工具类主要是对AI左右手进行辅助判断,因为模型的训练量不足,导致预测并不很准确。所以在这里使用一个队列工具类来获取最近三次的预测结果,之后再选择结果数最多的项来作为我们的预测结果。

public class QueueUtil {
  private static final ArrayBlockingQueue<Integer> handList = new ArrayBlockingQueue<>(3);
  private static int handLeft = 0;
  private static int handRight = 0;
  public static int getRecentHand(int labelHand) {
    int poll;
    if (!handList.offer(labelHand)) {
      poll = handList.remove();
      handList.offer(labelHand);
      if (poll == labelHand) {
        return compareRecentHand(handLeft, handRight);
      } else {
        switch (poll) {
        case OperatingHandClassifier.labelRight:
          handRight--;
          break;
        case OperatingHandClassifier.labelLeft:
          handLeft--;
          break;
        default:
          break;
        }
      }
    }
    switch (labelHand) {
    case OperatingHandClassifier.labelRight:
      handRight++;
      break;
    case OperatingHandClassifier.labelLeft:
      handLeft++;
      break;
    default:
      break;
    }
    return compareRecentHand(handLeft, handRight);
  }
  private static int compareRecentHand(int handLeft, int handRight) {
    if (handLeft > handRight) {
      return OperatingHandClassifier.labelLeft;
    } else {
      return OperatingHandClassifier.labelRight;
    }
  }
}
复制代码

封装到BaseActivity

为方便我们的使用,这里将调用模型进行预测的相关代码封装到 BaseActivity 中,等我们需要使用的时候,在继承其的相应 Activity 中加上该注解即可调用该功能。

关于该功能的完整代码,可以查看我们的大项目 dyjcow/qxy_potato at feature_AIDialog_DYJ (github.com)

public abstract class BaseActivity<P extends BasePresenter<? extends BaseView>, VB extends ViewBinding>
        extends AppCompatActivity implements BaseView, MotionEventTracker.ITrackDataReadyListener {
    /**
     * presenter层的引用
     */
    protected P presenter;
    private VB binding;
    private OperatingHandClassifier classifier;
    private MotionEventTracker tracker;
    public int hand = 1;
    /**
     * {@inheritDoc}
     * <p>
     * Perform initialization of all fragments.
     *
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (this.getClass().isAnnotationPresent(BindEventBus.class)) {
            EventBus.getDefault().register(this);
        }else if (this.getClass().isAnnotationPresent(InitAIHand.class)){
            classifier = new OperatingHandClassifier(this);
            classifier.checkAndInit();
            tracker = new MotionEventTracker(this);
            tracker.checkAndInit(this);
        }
        DisplayUtil.setCustomDensity(this);
        UltimateBarX.statusBarOnly(this)
                .light(true)
                .transparent()
                .apply();
        //强制使用竖屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        binding = ViewBindingUtil.inflateWithGeneric(this, getLayoutInflater());
        setContentView(binding.getRoot());
        presenter = createPresenter();
        initView();
        initData();
    }
    /**
     * 初始化presenter,也是与Activity的绑定
     *
     * @return 返回new的Presenter层的值
     */
    protected abstract P createPresenter();
    /**
     * 载入view的一些操作
     */
    protected abstract void initView();
    /**
     * 载入数据操作
     */
    protected abstract void initData();
    /**
     * 解除presenter与Activity的绑定
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (this.getClass().isAnnotationPresent(BindEventBus.class)) {
            EventBus.getDefault().unregister(this);
        }else if (this.getClass().isAnnotationPresent(InitAIHand.class)){
            classifier.close();
        }
        if (presenter != null) {
            presenter.detachView();
        }
    }
    @Override
    public void showLoading() {
        MyUtil.showLoading(this);
    }
    @Override
    public void SuccessHideLoading() {
        MyUtil.dismissSuccessLoading();
    }
    @Override
    public void FailedHideLoading() {
        MyUtil.dismissFailedLoading();
    }
    /**
     * 错误
     *
     * @param bean 错误信息
     */
    @Override
    public void onErrorCode(BaseBean bean) {
        ToastUtil.showToast(bean.msg);
    }
    public VB getBinding() {
        return binding;
    }
    /**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     * @return boolean Return true if this event was consumed.
     */
    @Override public boolean dispatchTouchEvent(MotionEvent ev) {
        if (tracker != null && ev != null){
            tracker.recordMotionEvent(ev);
        }
        return super.dispatchTouchEvent(ev);
    }
    @Override public void onTrackDataReady(@NonNull JSONArray dataList) {
        if (classifier != null){
            classifier.classifyAsync(dataList).addOnSuccessListener(result -> {
                hand = QueueUtil.getRecentHand(result.getLabelInt());
                LogUtil.d(MotionEventTracker.TAG,result.getLabel());
            }).addOnFailureListener(e -> LogUtil.e(MotionEventTracker.TAG,e.toString()));
        }
    }
}
复制代码

编写弹窗代码

该弹窗实际上使用了开源库实现滚动效果

Bigkoo/Android-PickerView: This is a picker view for android , support linkage effect, timepicker and optionspicker.(时间选择器、省市区三级联动) (github.com)

public class MyUtil{
    ...
  public static void showOneOptionPicker(List<?> list, int handLabel) {
        OptionsPickerBuilder builder = new OptionsPickerBuilder(ActivityUtil.getCurrentActivity(),
                (options1, options2, options3, v) -> {
                    //返回的分别是三个级别的选中位置
                    BaseEvent<?> event = new BaseEvent<>(EventCode.SELECT_VERSION, list.get(options1));
                    EventBusUtil.sendEvent(event);
                });
        pvOptions = builder
                .setDividerColor(Color.BLACK)
                .setTextColorCenter(Color.BLACK) //设置选中项文字颜色
                .setContentTextSize(19)
                .setDividerColor(Color.GRAY)
                .setDividerType(WheelView.DividerType.WRAP)
                .isAlphaGradient(true)
                .setLayoutRes(R.layout.layout_pickview_dialog, v -> {
                    //根据传入的左右手的值来选择对应的位置控件
                    TextView textView;
                    if (handLabel == OperatingHandClassifier.labelRight){
                        textView = v.findViewById(R.id.btnSubmitRight);
                    }else {
                        textView = v.findViewById(R.id.btnSubmitLeft);
                    }
                    //设置好控件后,让其显示
                    textView.setVisibility(View.VISIBLE);
                    textView.setOnClickListener(v1 -> {
                        pvOptions.returnData();
                        pvOptions.dismiss();
                    });
                })
                .build();
        pvOptions.setPicker(list);//一级选择器
        pvOptions.show();
    }
    ...
}
复制代码

下面我们看一下对应的布局代码

这里是按照原控件的布局代码来做的一个该着,下边的 WheelView 仍旧使用的是三个,未做优化改造。

<?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="wrap_content"
        android:background="@drawable/shape_sheet_dialog_bg_white">
    <TextView
            android:id="@+id/btnSubmitLeft"
            android:text="@string/pick_submit"
            android:textStyle="bold"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="invisible"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/optionspicker"
            android:layout_marginStart="20dp"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginBottom="10dp"
            android:layout_marginTop="15dp" />
    <TextView
            android:id="@+id/btnSubmitRight"
            android:text="@string/pick_submit"
            android:textStyle="bold"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="invisible"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/optionspicker"
            android:layout_marginEnd="20dp"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginBottom="10dp"
            android:layout_marginTop="15dp" />
    <LinearLayout
            android:id="@+id/optionspicker"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white"
            android:gravity="center"
            android:minHeight="180dp"
            android:orientation="horizontal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/btnSubmitRight">
        <com.contrarywind.view.WheelView
                android:id="@+id/options1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1" />
        <com.contrarywind.view.WheelView
                android:id="@+id/options2"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1" />
        <com.contrarywind.view.WheelView
                android:id="@+id/options3"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
复制代码

效果

1.webp.jpg1.webp.jpg

当我们左手点击的时候,确认按钮在左边,右手点击的时候,确认按钮在右边

相关文章
|
18天前
|
移动开发 开发框架 小程序
轻松搭建婚恋交友系统源码,H5/小程序/APP自动适配,智能匹配恋爱交友平台快速落地
婚恋交友系统涵盖在线交友、线下活动、专业服务、社交娱乐等,满足用户多样化需求。系统设计简洁易用,提供实名认证、多注册方式及安全保护,确保用户隐私和数据安全。功能丰富,支持图文展示、筛选匹配、聊天互动、虚拟礼物等,提升互动趣味性。平台可分类管理用户、审核信息、智能推荐,优化用户体验。基于TP6+Uni-app框架,实现跨平台同步,支持二次开发,适应不同市场需求。 [了解更多](https://gitee.com/multi-customer-software/jy)
|
5月前
|
Web App开发 Java 视频直播
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
对于软件、计算机等专业的毕业生,毕业设计需实现实用软件或APP。新颖的设计应结合最新技术,如5G时代的音视频技术。示例包括: 1. **短视频分享APP**: 集成FFmpeg实现视频剪辑功能,如添加字幕、转场特效等。 2. **电商购物APP**: 具备直播带货功能,使用RTMP/SRT协议支持流畅直播体验。 3. **同城生活APP**: 引入WebRTC技术实现可信的视频通话功能。这些应用不仅实用,还能展示开发者紧跟技术潮流的能力。
104 4
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
|
6月前
|
Web App开发 Android开发
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
实时数据传输在互联网中至关重要,不仅支持即时通讯如QQ、微信的文字与图片传输,还包括音视频通信。一对一通信常采用WebRTC技术,如《Android Studio开发实战》中的App集成示例;而一对多的在线直播则需部署独立的流媒体服务器,使用如SRT等协议。SRT因其优越的直播质量正逐渐成为主流。本文档概述了SRT协议的使用,包括通过OBS Studio和SRT Streamer进行SRT直播推流的方法,并展示了推流与拉流的成功实例。更多细节参见《FFmpeg开发实战》一书。
97 1
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
|
6月前
|
Web App开发 5G Linux
FFmpeg开发笔记(四十四)毕业设计可做的几个拉满颜值的音视频APP
一年一度的毕业季来临,计算机专业的毕业设计尤为重要,不仅关乎学业评价还积累实战经验。选择紧跟5G技术趋势的音视频APP作为课题极具吸引力。这里推荐三类应用:一是融合WebRTC技术实现视频通话的即时通信APP;二是具备在线直播功能的短视频分享平台,涉及RTMP/SRT等直播技术;三是具有自定义动画特效及卡拉OK歌词字幕功能的视频剪辑工具。这些项目不仅技术含量高,也符合市场需求,是毕业设计的理想选择。
109 6
FFmpeg开发笔记(四十四)毕业设计可做的几个拉满颜值的音视频APP
|
6月前
|
编解码 Java Android开发
FFmpeg开发笔记(四十五)使用SRT Streamer开启APP直播推流
​SRT Streamer是一个安卓手机端的开源SRT协议直播推流框架,可用于RTMP直播和SRT直播。SRT Streamer支持的视频编码包括H264、H265等等,支持的音频编码包括AAC、OPUS等等,可谓功能强大的APP直播框架。另一款APP直播框架RTMP Streamer支持RTMP直播和RTSP直播,不支持SRT协议的直播。而本文讲述的SRT Streamer支持RTMP直播和SRT直播,不支持RTSP协议的直播。有关RTMP Streamer的说明参见之前的文章《使用RTMP Streamer开启APP直播推流》,下面介绍如何使用SRT Streamer开启手机直播。
109 4
FFmpeg开发笔记(四十五)使用SRT Streamer开启APP直播推流
|
7月前
|
Web App开发 缓存 编解码
FFmpeg开发笔记(三十八)APP如何访问SRS推流的RTMP直播地址
《FFmpeg开发实战》书中介绍了轻量级流媒体服务器MediaMTX,适合测试RTSP/RTMP协议,但不适用于复杂直播场景。SRS是一款强大的开源流媒体服务器,支持多种协议,起初为RTMP,现扩展至HLS、SRT等。在FFmpeg 6.1之前,推送给SRS的HEVC流不受支持。要播放RTMP流,Android应用可使用ExoPlayer,需在`build.gradle`导入ExoPlayer及RTMP扩展,并根据URL类型创建MediaSource。若SRS播放黑屏,需在配置文件中开启`gop_cache`以缓存关键帧。
190 2
FFmpeg开发笔记(三十八)APP如何访问SRS推流的RTMP直播地址
|
8月前
|
编解码 Java Android开发
FFmpeg开发笔记(三十一)使用RTMP Streamer开启APP直播推流
RTMP Streamer是一款开源的安卓直播推流框架,支持RTMP、RTSP和SRT协议,适用于各种直播场景。它支持H264、H265、AV1视频编码和AAC、G711、OPUS音频编码。本文档介绍了如何使用Java版的RTMP Streamer,建议使用小海豚版本的Android Studio (Dolphin)。加载项目时,可添加国内仓库加速依赖下载。RTMP Streamer包含五个模块:app、encoder、rtmp、rtplibrary和rtsp。完成加载后,可以在手机上安装并运行APP,提供多种直播方式。开发者可以从《FFmpeg开发实战:从零基础到短视频上线》获取更多信息。
155 7
FFmpeg开发笔记(三十一)使用RTMP Streamer开启APP直播推流
|
6月前
|
测试技术
一款功能完善的智能匹配1V1视频聊天App应该通过的测试CASE
文章列举了一系列针对1V1视频聊天App的测试用例,包括UI样式、权限请求、登录流程、匹配逻辑、消息处理、充值功能等多个方面的测试点,并标注了每个测试用例的执行状态,如通过(PASS)、失败(FAIL)或需要进一步处理(延期修改、待定、方案再定等)。
95 0
|
8月前
|
移动开发 小程序 视频直播
FFmpeg开发笔记(二十七)解决APP无法访问ZLMediaKit的直播链接问题
本文讲述了在使用ZLMediaKit进行视频直播时,遇到移动端通过ExoPlayer和微信小程序播放HLS直播地址失败的问题。错误源于ZLMediaKit对HTTP地址的Cookie校验导致401无权限响应。通过修改ZLMediaKit源码,注释掉相关鉴权代码并重新编译安装,解决了此问题,使得ExoPlayer和小程序能成功播放HLS视频。详细解决方案及FFmpeg集成可参考《FFmpeg开发实战:从零基础到短视频上线》一书。
317 3
FFmpeg开发笔记(二十七)解决APP无法访问ZLMediaKit的直播链接问题
|
9月前
|
人工智能 UED iOS开发
最佳平替APP:智能消费新选择
【2月更文挑战第29天】最佳平替是一款AI应用,响应消费降级趋势,通过智能匹配帮用户找到价低质优的商品替代品,节省开支。用户输入商品名,AI推荐相似平替选项,提高购物效率。涵盖商品、旅游景点、学校等多个领域,提供跨界平替建议。尽管AI推荐有时不准确,开发团队正持续优化,旨在帮助用户理性消费,避免不必要的开支,已获得用户支持。
206 1
最佳平替APP:智能消费新选择

热门文章

最新文章

  • 1
    DeepSeek Artifacts:在线实时预览的前端 AI 编程工具,基于DeepSeek V3快速生成React App
  • 2
    【05】2025年1月首发完整版-篇幅较长-苹果app如何上架到app store完整流程·不借助第三方上架工具的情况下无需花钱但需仔细学习-优雅草央千澈详解关于APP签名以及分发-们最关心的一篇来了-IOS上架app
  • 3
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 4
    【Azure Logic App】使用MySQL 新增行触发器遇见错误 :“Unknown column 'created_at' in 'order clause'”
  • 5
    【Azure Function】Function App出现System.IO.FileNotFoundException异常
  • 6
    电竞陪玩系统架构优化设计,陪玩app如何提升系统稳定性,陪玩小程序平台的测试与监控
  • 7
    【Azure App Service】对App Service中CPU指标数据中系统占用部分(System CPU)的解释
  • 8
    微信小程序 app.json 配置文件解析与应用
  • 9
    【01】vs-code如何配置flutter环境-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈-供大大的学习提升
  • 10
    轻松搭建婚恋交友系统源码,H5/小程序/APP自动适配,智能匹配恋爱交友平台快速落地
  • 1
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    35
  • 2
    【Azure Function】Function App出现System.IO.FileNotFoundException异常
    95
  • 3
    【Azure Logic App】使用MySQL 新增行触发器遇见错误 :“Unknown column 'created_at' in 'order clause'”
    101
  • 4
    阿里云APP备案流程图以及备案所需材料整理,跟着教程一步步操作
    39
  • 5
    1688app 商品详情接口系列(1688API)
    55
  • 6
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    48
  • 7
    【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    18
  • 8
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    109
  • 9
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
    47
  • 10
    Django—同一项目不同app使用不同数据库
    16