赞
踩
选择Navigation Drawer View Activity
打开app目录下的build.gradle文件
plugins { id 'com.android.application' } android { namespace 'com.example.bluetoothled' compileSdk 33 defaultConfig { applicationId "com.example.bluetoothled" minSdk 29 targetSdk 33 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } buildFeatures { viewBinding true } } dependencies { implementation 'androidx.appcompat:appcompat:1.6.0' implementation 'com.google.android.material:material:1.8.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' implementation 'androidx.navigation:navigation-fragment:2.5.3' implementation 'androidx.navigation:navigation-ui:2.5.3' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' implementation 'org.videolan.android:libvlc-all:3.4.4' implementation 'me.aflak.libraries:bluetooth:1.3.9' }
安装dependence{ }中的依赖库,具体版本可以根据自己的需求改变,但是有可能导致某些库报错。
此文件主要最为目录导航页面,提供界面进行页面跳转。
<?xml version="1.0" encoding="utf-8"?> <androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:openDrawer="start"> <include android:id="@+id/app_bar_main" layout="@layout/app_bar_main" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" /> </androidx.drawerlayout.widget.DrawerLayout>
此文件提供蓝牙连接活动按钮,进行蓝牙连接。并且此页面浮于content_main.xml文件之上。
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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" tools:context=".MainActivity"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/Theme.Bluetoothled.AppBarOverlay"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/Theme.Bluetoothled.PopupOverlay" > </androidx.appcompat.widget.Toolbar> </com.google.android.material.appbar.AppBarLayout> <include layout="@layout/content_main" />//导入content_main视图 <CheckBox android:id="@+id/Bluetooth_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="130dp" android:layout_marginTop="50dp" /> <CheckBox android:id="@+id/Focus_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="340dp" android:layout_marginTop="50dp" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginEnd="@dimen/fab_margin" android:layout_marginBottom="16dp" app:srcCompat="@drawable/bt" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
提供文件导航
<?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" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/app_bar_main"> <fragment android:id="@+id/nav_host_fragment_content_main" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/mobile_navigation" /> </androidx.constraintlayout.widget.ConstraintLayout>
提供好看的导航背景
<?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" android:layout_width="match_parent" android:layout_height="@dimen/nav_header_height" android:background="@drawable/side_nav_bar" android:gravity="bottom" android:orientation="vertical" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:theme="@style/ThemeOverlay.AppCompat.Dark"> <ImageView android:id="@+id/imageView" android:layout_width="105dp" android:layout_height="81dp" android:contentDescription="@string/nav_header_desc" android:paddingTop="@dimen/nav_header_vertical_spacing" app:srcCompat="@drawable/bt" /> </LinearLayout>
主要操作界面布局,主要的蓝牙活动和VLC播放器都放在此页面,作为目录分级下的页面。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".ui.m01.Author.AuthorFragment"> <LinearLayout android:layout_width="match_parent" android:layout_height="30dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" android:ellipsize="end" android:gravity="center" android:maxLines="1" android:text="蓝牙连接:" android:textStyle="bold" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="30dp" android:layout_weight="1" android:gravity="center" android:paddingLeft="15dp" android:text="对焦状态:" android:textStyle="bold" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="300dp" android:orientation="horizontal"> <org.videolan.libvlc.util.VLCVideoLayout android:id = "@+id/videoLayout" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="100dp" android:orientation="horizontal"/> <LinearLayout android:layout_width="match_parent" android:layout_height="50dp" android:orientation="horizontal"> <Button android:id="@+id/auto_focusing" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:text="自动对焦" /> <Button android:id="@+id/up" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:text="上" /> <Button android:id="@+id/manual_focusing" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginRight="10dp" android:text="手动对焦" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="50dp" android:orientation="horizontal"> <Button android:id="@+id/left" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:text="左" /> <Button android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:text="停" /> <Button android:id="@+id/right" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:text="右" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="50dp" android:orientation="horizontal"> <Button android:id="@+id/pztneg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:text="近" tools:ignore="TouchTargetSizeCheck" /> <Button android:id="@+id/down" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:text="远" /> <Button android:id="@+id/pztpos" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:text="近" tools:ignore="TouchTargetSizeCheck" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="59dp" android:orientation="horizontal"> <Button android:id="@+id/slow_select" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_weight="1" android:text="慢" tools:ignore="TouchTargetSizeCheck" /> <Button android:id="@+id/play" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_weight="1" android:text="播放" tools:ignore="TouchTargetSizeCheck" /> <Button android:id="@+id/fast_select" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_weight="1" android:text="快" tools:ignore="TouchTargetSizeCheck" /> </LinearLayout> </LinearLayout>
此文件的主要功能为:
·连接蓝牙
·判断蓝牙连接状态
·根据返回状态打印
package com.example.bluetoothled; import android.bluetooth.BluetoothSocket; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Menu; import android.widget.CheckBox; import android.widget.TextView; import android.widget.Toast; import com.example.bluetoothled.databinding.FragmentM01AuthorBinding; import com.example.bluetoothled.ui.m01.Author.AuthorFragment; import com.example.bluetoothled.util.ByteReader; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.navigation.NavigationView; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.AppCompatActivity; import com.example.bluetoothled.databinding.ActivityMainBinding; import me.aflak.bluetooth.Bluetooth; public class MainActivity extends AppCompatActivity { private AppBarConfiguration mAppBarConfiguration; private ActivityMainBinding binding; MainViewModel mainModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mainModel= new ViewModelProvider(this).get(MainViewModel.class); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.appBarMain.toolbar); //Interlocking with the menu (navigation) DrawerLayout drawer = binding.drawerLayout; NavigationView navigationView = binding.navView; // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. mAppBarConfiguration = new AppBarConfiguration.Builder( R.id.nav_author, R.id.nav_thinkercad, R.id.nav_wokwi,R.id.nav_proteus, R.id.nav_led, R.id.nav_joystick) .setOpenableLayout(drawer) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration); NavigationUI.setupWithNavController(navigationView, navController); permissionGranted(); Bluetooth bluetooth = new Bluetooth(this);//1 bluetooth.setReader(ByteReader.class); //Need to set Bluetooth byte reader //2 mainModel.setBluetooth(bluetooth); //Received from mainModel Same as setting connect status information. Log.d("###","main.oncreate"); binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mainModel.bluetooth.isConnected()) Snackbar.make(view, "HUAWEI_MATE_AF", Snackbar.LENGTH_LONG) .setAction("Disconnect", v -> { mainModel.bluetooth.disconnect(); }).show(); else Snackbar.make(view, "HUAWEI_MATE_AF", Snackbar.LENGTH_LONG) .setAction("Connect", v->{ mainModel.bluetooth.connectToName("HUAWEI_MATE_AF"); }).show(); } }); mainModel.bluetoothStatus.observe(this, status->{ Toast.makeText(this, status, Toast.LENGTH_SHORT).show(); if(status.equals("Connected")) { mainModel.firmata.sendString(status); binding.appBarMain.BluetoothState.setChecked(true); } if (status.equals("received")){ binding.appBarMain.FocusState.setChecked(true); } }); } //3 @Override protected void onStart() { super.onStart(); mainModel.bluetooth.onStart(); } //4 @Override protected void onStop() { super.onStop(); mainModel.bluetooth.onStop(); } @Override public boolean onSupportNavigateUp() { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp(); } void permissionGranted() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { requestPermissions( new String[]{ android.Manifest.permission.BLUETOOTH, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT }, 1);//1是需要的 } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions( new String[]{ android.Manifest.permission.BLUETOOTH }, 1);//1是需要的 } } }
提供判断蓝牙连接状态函数,根据此文件返回值进行后续判断。
package com.example.bluetoothled; import android.bluetooth.BluetoothDevice; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import com.example.bluetoothled.util.Firmata; import com.example.bluetoothled.util.FirmataVersionData; import me.aflak.bluetooth.Bluetooth; import me.aflak.bluetooth.interfaces.DeviceCallback; public class MainViewModel extends ViewModel { Bluetooth bluetooth; public Firmata firmata; public MutableLiveData<String> bluetoothStatus = new MutableLiveData<>(); public MutableLiveData<FirmataVersionData> firmataVersionData = new MutableLiveData<>(); public MainViewModel(){ bluetoothStatus.setValue("Disconnected"); firmataVersionData.setValue(new FirmataVersionData()); } void setBluetooth(Bluetooth bluetooth){ this.bluetooth = bluetooth; firmata =new Firmata(this.bluetooth); bluetooth.setDeviceCallback(new DeviceCallback() { @Override public void onDeviceConnected(BluetoothDevice device) { bluetoothStatus.postValue("Connected"); } @Override public void onDeviceDisconnected(BluetoothDevice device, String message) { bluetoothStatus.postValue("Disconnected"); } @Override public void onMessage(byte[] message) { bluetoothStatus.postValue("received"); } @Override public void onError(int errorCode) { bluetoothStatus.postValue("Error"); } @Override public void onConnectError(BluetoothDevice device, String message) { bluetoothStatus.postValue("Connect Error"); } }); firmata.attach((m,n)->{ FirmataVersionData version = new FirmataVersionData(); version.setMajor(m); version.setMinor(n); firmataVersionData.postValue(version); }); } public Bluetooth getBluetooth(){ return bluetooth; } }
定义了一些按钮和VLC视频播放器,主要收发功能在此实现。
package com.example.bluetoothled.ui.m01.Author; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import com.example.bluetoothled.MainViewModel; import com.example.bluetoothled.R; import com.example.bluetoothled.databinding.FragmentM01AuthorBinding; import android.net.Uri; import org.videolan.libvlc.LibVLC; import org.videolan.libvlc.Media; import org.videolan.libvlc.MediaPlayer; import org.videolan.libvlc.util.VLCVideoLayout; public class AuthorFragment extends Fragment { private FragmentM01AuthorBinding binding; AuthorViewModel Model; MainViewModel mainModel; //private static final String url = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"; private static final String url = "rtsp://192.168.1.3:554/"; private LibVLC libVlc; private MediaPlayer mediaPlayer; private VLCVideoLayout videoLayout; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Model = new ViewModelProvider(this).get(AuthorViewModel.class); mainModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); binding = FragmentM01AuthorBinding.inflate(inflater, container, false); return binding.getRoot(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); libVlc = new LibVLC(requireActivity()); mediaPlayer = new MediaPlayer(libVlc); videoLayout = view.findViewById(R.id.videoLayout); binding.up.setOnClickListener(v->{ mainModel.firmata.sendSysex("u"); }); binding.down.setOnClickListener(v->{ mainModel.firmata.sendSysex("d"); }); binding.left.setOnClickListener(v->{ mainModel.firmata.sendSysex("l"); }); binding.right.setOnClickListener(v->{ mainModel.firmata.sendSysex("r"); }); binding.stop.setOnClickListener(v->{ mainModel.firmata.sendSysex("s"); }); binding.pztneg.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { // 按下时发送命令 mainModel.firmata.sendSysex("N"); Toast.makeText(getContext(),"pressed",Toast.LENGTH_SHORT).show(); } else if (event.getAction() == MotionEvent.ACTION_UP) { // 松开时发送命令 mainModel.firmata.sendSysex("T"); Toast.makeText(getContext(),"released",Toast.LENGTH_SHORT).show(); } return false; } }); binding.pztpos.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { // 按下时发送命令 mainModel.firmata.sendSysex("P"); } else if (event.getAction() == MotionEvent.ACTION_UP) { // 松开时发送命令 mainModel.firmata.sendSysex("T"); } return false; } }); binding.autoFocusing.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mainModel.firmata.sendSysex("a"); } }); binding.manualFocusing.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mainModel.firmata.sendSysex("m"); } }); binding.slowSelect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mainModel.firmata.sendSysex("L"); //bluetoothState.setChecked(true); //binding.BluetoothState.setChecked(true); } }); binding.fastSelect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mainModel.firmata.sendSysex("H"); } }); binding.play.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startVideoPlayback(); } }); } private void startVideoPlayback() { mediaPlayer.attachViews(videoLayout, null, false, false); Media media = new Media(libVlc, Uri.parse(url)); media.setHWDecoderEnabled(true, false); media.addOption(":network-caching=600"); mediaPlayer.setMedia(media); media.release(); mediaPlayer.play(); } @Override public void onStop() { super.onStop(); mediaPlayer.stop(); mediaPlayer.detachViews(); } @Override public void onDestroy() { super.onDestroy(); mediaPlayer.release(); libVlc.release(); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } }
package com.example.bluetoothled.ui.m01.Author; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class AuthorViewModel extends ViewModel { private final MutableLiveData<String> mText; public AuthorViewModel() { mText = new MutableLiveData<>(); mText.setValue("This is home fragment"); } public LiveData<String> getText() { return mText; } }
具体可以参考提供的项目文件,各个功能块之间分的比较清楚,能够容易看懂。
项目连接:链接:https://pan.baidu.com/s/1JkeSidV0jrrekTUAITEfwg?pwd=gi69
提取码:gi69
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。