如何让你的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

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

相关文章
|
3月前
|
Web App开发 Java 视频直播
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
对于软件、计算机等专业的毕业生,毕业设计需实现实用软件或APP。新颖的设计应结合最新技术,如5G时代的音视频技术。示例包括: 1. **短视频分享APP**: 集成FFmpeg实现视频剪辑功能,如添加字幕、转场特效等。 2. **电商购物APP**: 具备直播带货功能,使用RTMP/SRT协议支持流畅直播体验。 3. **同城生活APP**: 引入WebRTC技术实现可信的视频通话功能。这些应用不仅实用,还能展示开发者紧跟技术潮流的能力。
100 4
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
|
4月前
|
Web App开发 Android开发
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
实时数据传输在互联网中至关重要,不仅支持即时通讯如QQ、微信的文字与图片传输,还包括音视频通信。一对一通信常采用WebRTC技术,如《Android Studio开发实战》中的App集成示例;而一对多的在线直播则需部署独立的流媒体服务器,使用如SRT等协议。SRT因其优越的直播质量正逐渐成为主流。本文档概述了SRT协议的使用,包括通过OBS Studio和SRT Streamer进行SRT直播推流的方法,并展示了推流与拉流的成功实例。更多细节参见《FFmpeg开发实战》一书。
82 1
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
|
4月前
|
Web App开发 5G Linux
FFmpeg开发笔记(四十四)毕业设计可做的几个拉满颜值的音视频APP
一年一度的毕业季来临,计算机专业的毕业设计尤为重要,不仅关乎学业评价还积累实战经验。选择紧跟5G技术趋势的音视频APP作为课题极具吸引力。这里推荐三类应用:一是融合WebRTC技术实现视频通话的即时通信APP;二是具备在线直播功能的短视频分享平台,涉及RTMP/SRT等直播技术;三是具有自定义动画特效及卡拉OK歌词字幕功能的视频剪辑工具。这些项目不仅技术含量高,也符合市场需求,是毕业设计的理想选择。
100 6
FFmpeg开发笔记(四十四)毕业设计可做的几个拉满颜值的音视频APP
|
4月前
|
编解码 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开启手机直播。
94 4
FFmpeg开发笔记(四十五)使用SRT Streamer开启APP直播推流
|
5月前
|
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`以缓存关键帧。
174 2
FFmpeg开发笔记(三十八)APP如何访问SRS推流的RTMP直播地址
|
4月前
|
测试技术
一款功能完善的智能匹配1V1视频聊天App应该通过的测试CASE
文章列举了一系列针对1V1视频聊天App的测试用例,包括UI样式、权限请求、登录流程、匹配逻辑、消息处理、充值功能等多个方面的测试点,并标注了每个测试用例的执行状态,如通过(PASS)、失败(FAIL)或需要进一步处理(延期修改、待定、方案再定等)。
73 0
|
6月前
|
编解码 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开发实战:从零基础到短视频上线》获取更多信息。
141 7
FFmpeg开发笔记(三十一)使用RTMP Streamer开启APP直播推流
|
6月前
|
移动开发 小程序 视频直播
FFmpeg开发笔记(二十七)解决APP无法访问ZLMediaKit的直播链接问题
本文讲述了在使用ZLMediaKit进行视频直播时,遇到移动端通过ExoPlayer和微信小程序播放HLS直播地址失败的问题。错误源于ZLMediaKit对HTTP地址的Cookie校验导致401无权限响应。通过修改ZLMediaKit源码,注释掉相关鉴权代码并重新编译安装,解决了此问题,使得ExoPlayer和小程序能成功播放HLS视频。详细解决方案及FFmpeg集成可参考《FFmpeg开发实战:从零基础到短视频上线》一书。
278 3
FFmpeg开发笔记(二十七)解决APP无法访问ZLMediaKit的直播链接问题
|
7月前
|
人工智能 UED iOS开发
最佳平替APP:智能消费新选择
【2月更文挑战第29天】最佳平替是一款AI应用,响应消费降级趋势,通过智能匹配帮用户找到价低质优的商品替代品,节省开支。用户输入商品名,AI推荐相似平替选项,提高购物效率。涵盖商品、旅游景点、学校等多个领域,提供跨界平替建议。尽管AI推荐有时不准确,开发团队正持续优化,旨在帮助用户理性消费,避免不必要的开支,已获得用户支持。
183 1
最佳平替APP:智能消费新选择
|
7月前
|
传感器 内存技术
毕业设计 江科大STM32的智能温室控制蓝牙声光报警APP系统设计
毕业设计 江科大STM32的智能温室控制蓝牙声光报警APP系统设计
211 0

热门文章

最新文章