试了两天,不管是deepin 软件仓库自带的eclipse,还是java 版的idea,怎样安装android 都不成功,反正各样的错误。最后到android官网下载了最新的集成开发软件才成功。官网网址
https://developer.android.com/studio
下载了linux 版:android-studio-2022.2.1.20-linux.tar.gz
按照安装文档轻松安装。建议,最好让电脑科学上网,因为有很多个文件是从google网站上下载的。
还有最好不要用虚拟终端输出程序结果。因为速度很慢。找一个旧手机代替,速度快得多。
下面的代码是输入框的内容显示在文本框中。
以下的程序都采用的是Empty Views Activity 模板。
发现android编程,ui界面部分只要把布局编辑器练熟就行了,难的还是代码的事件处理部份。ui的很多控件初学都用不上,只要把三四个常用的搞熟就可以练代码了。
package com.example.wz;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //按照布局文件activity_main.xml 显示,估计此方法是不停刷新此文件的内容,所以程序不用再setContentView
Button button=findViewById(R.id.button2); //android 用findViewById 取得java的控件实例
EditText editText=findViewById(R.id.editTextText); //editText 对应的布局编译器中为Plain Text 文本输入框
TextView textView=findViewById(R.id.textView3);
button.setOnClickListener(view -> { //按键的响应
String s = String.valueOf(editText.getText()); //取文本框输入内容
textView.setText(s);
});
}
}
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
如要连接网络,必须在AndroidManifest.xml文件中加上此两句
下面的代码是连接服务器
package com.example.wz;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //按照布局文件activity_main.xml 显示,估计此方法是不停刷新此文件的内容,所以程序多次setContentView()。
Button button=findViewById(R.id.button2); //android 用findViewById 取得java的控件实例
EditText editText=findViewById(R.id.editTextText); //editText 对应的布局编译器中为Plain Text 文本输入框
TextView textView=findViewById(R.id.textView3);
button.setOnClickListener(view -> { //按键的响应
// String s = String.valueOf(editText.getText()); //取文本框输入内容
// textView.setText("接收数据");
try {
Socket socket=new Socket("192.168.43.61",3000);
InputStream inputStream=socket.getInputStream();
ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
String s=objectInputStream.readUTF();
textView.setText(s);
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
注意:try catch语句要注释掉throw new RunimeException(e);这句,否则一有错,程序就退出。有这个基础程序框架,就可以试试把java程序搬到手机上。
还有setContentView()方法,估计安卓系统是定时不停地刷新桌面显示控件,有点像java的Timer,所以程序不用多次用此方法,一次就行了。
android 线程间传递数据,不能用java 线程的传递方法,如静态类变量就不能用。
package com.example.wz;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
static Handler handler;
static TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
EditText editText = findViewById(R.id.editTextText);
textView = findViewById(R.id.textView);
Handler.Callback hc=new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) { //这里必须调用handle类的镶嵌类Handle.Callback的handleMessage方法。
int t=msg.arg1;
textView.setText(String.valueOf(t));
return false;
}
};
handler =new Handler(Looper.myLooper(),hc);
button.setOnClickListener(view -> {
Th th = new Th();
th.start();
});
}
}
class Th extends Thread {
int t=0;
public void run() {
while (true) {
Message message = new Message();
message.arg1=t;
MainActivity.handler.sendMessage(message);
t++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// throw new RuntimeException(e);
}
}
}
}
此种传递数据的方法,数据的产生和ui显示是异步的。我理解有点像map 的键值对,这个ss就是键,接照键的对应关系,线程先把值存在”map”中,ui再取出此值显示。这样理解好记忆。
安卓的主线程最好只处现ui代码和事件响应代码,其他的java逻辑算法代码都不要放在主线程中,java代码最好放在响应事件的代码块中,或者放在其他子线程中。而且响应事件的代码耗时不能太久,更不能放循环等待的语句,如有这种耗时的代码,必须放在新线程中,用事件响应启动此线程。如按钮单击启动耗时线程。
其实这也是一个android编程通用方法,把ui代码放在主线程中,其他的一切逻辑代码另开一个线程完成,主线程用一个事件响应启动这个新线程,新线程的数据通过上面程序的方法传给主线程完成显示。这样处理事后容易理解看清思路。这是我自己的看法。当然你也可以在主线程中混编,只要能通过不报错。
看jdk学Handle
Handler 允许您发送和处理
Message
与线程关联的 Runnable 对象
MessageQueue
。每个 Handler 实例都与一个线程和该线程的消息队列相关联。当您创建一个新的处理程序时,它会绑定到一个
Looper
. 它会将消息和可运行对象传递到该 Looper 的消息队列,并在该 Looper 的线程上执行它们。
Handler 有两个主要用途:(1) 安排消息和可运行对象在未来某个时间点执行;(2) 将要在与您自己的线程不同的线程上执行的操作排队。
调度消息是通过
post(Runnable)
、
postAtTime(java.lang.Runnable, long)
、
postDelayed(Runnable, Object, long)
、
sendEmptyMessage(int)
、
sendMessage(Message)
、
sendMessageAtTime(Message, long)
和
sendMessageDelayed(Message, long)
方法完成的。post版本允许您将 Runnable 对象入队,以便在接收到消息队列时调用它们
;
sendMessage版本允许您将包含
将由
Message
Handler 的方法处理的数据包的对象排入队列
handleMessage(Message)
(要求您实现 Handler 的子类)。
发布或发送到处理程序时,您可以允许项目在消息队列准备就绪后立即处理,或者指定处理前的延迟或处理的绝对时间。后两者允许您实现超时、滴答和其他基于时间的行为。
handle post() 方法,发现只能更新一次,不知道是否正确
此方法代码很简单
package com.example.wz;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
static Handler handler=new Handler();
static TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
EditText editText = findViewById(R.id.editTextText);
textView = findViewById(R.id.textView);
button.setOnClickListener(view -> {
Th th = new Th();
th.start();
});
}
}
class Th extends Thread {
public void run() {
MainActivity.handler.post(()->{
MainActivity.textView.setText("hello nanning");
});
}
}
这里的handle实例必须放在主线程中,其实把它想成java场景中的静态类变量就好理解了。
如要循环,可以这样处理,但又不能加Thread.sleep()延时
class Th extends Thread { //刚实验,此post方法只能更新一次ui。如加循环,报错
public void run () {
while(true) {
h.post(new Runnable() {
public void run() {
textView.setText(String.valueOf(1));
}
});
h.post(new Runnable() {
public void run() {
textView.setText(String.valueOf(2));
}
});
}
}
}
这个post方法虽然只能执行一次更新ui,也可编写许多场景的程序了。如连接服务器一次读取数据,输入网址读取网页文件,查询一次文件目录,如是线程不停传递数据则不能用此方法。
利用handle.post()连接服务器读取一次数据
package com.example.wz;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
static Handler h = new Handler();
static TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
EditText editText = findViewById(R.id.editTextText);
textView = findViewById(R.id.textView);
button.setOnClickListener(view -> {
Th th = new Th();
th.start();
});
}
}
class Th extends Thread { //刚实验,此post方法只能更新一次ui。如加循环,报错
public void run() {
Socket socket= null;
String s;
try {
socket = new Socket("192.168.2.8",3000);
InputStream inputStream=socket.getInputStream();
ObjectInputStream objectInputStream=new
ObjectInputStream(inputStream);
s=objectInputStream.readUTF();
MainActivity.h.post(new Runnable() {
public void run() {
MainActivity.textView.setText(s);
}
});
objectInputStream.close();
} catch (IOException e) {
// throw new RuntimeException(e);
}
}
}
手机要是上网,必须要在全局文件中加这俩句
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
=========================================================================
android 文件
参考出处:https://blog.csdn.net/huweiliyi/category_8913738.html
raw目录
asserts目录
data/data/包名
sdcard目录
其中raw下的和asserts下的文件可以以资源文件的形式读取,这些目录的数据只能读取,不能写入,两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
data/data/包名和sdcard目录文件可读可写。其中data/data/包名目录不需要申请权限,sdcard目录需要申请权限
读取文件要在全局文件中加这两句
<!--文件读写权限--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
data/data/(包名) 目录文件读写
将数据以普通文件的形式保存在 /data/data/包名中,该方法不需要申请权限。
存放在数据区(/data/data/包名)的文件可以使用openFileOutput和openFileInput进行操作。这两个类只能在主线程中使用。
data/data/包名/shared_prefs
存放SharedPreferences数据
data/data/包名/databases
存放数据库数据
data/data/包名/files
存放普通数据
data/data/包名/cache
存放缓存文件
开发IDE 右边有一个Device File Explorer 用它可以浏览手机的整个目录结构
可以用它看/data/data/ 有啥文件,再读取。
建立文件,
发现只能在/data/data/本包名/下创建文件,在其他地方都报错
。所以就不存在查找其他目录内容的必要,反正生成的文件必定在自己本程序的包名下。
还发现android的openFileOutput 不能在子线程中使用,只能在主线程中用。所以android的主线程也可编辑非ui代码。但主线程各种代码太多太杂,不好理解分析。网上一大批文章代码,本可以顺序写的,却搞几个类或方法跳过去跳过来,让人看的思路都中断了。
还有如用ide中的
Device File Explorer检验是否生成文件,执行程序后必须断开连接手机终端usb,再连接才会正确显示。
package com.example.wz;
import static android.content.Context.MODE_PRIVATE;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
static Handler handler=new Handler();
static TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
EditText editText = findViewById(R.id.editTextText);
textView = findViewById(R.id.textView);
button.setOnClickListener(view -> {
Th th = new Th();
th.start();
});
}
}
class Th extends Thread {
public void run() {
String text = "hello nanning";
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream("/data/data/com.example.wz/2.txt");
fileOutputStream.write(text.getBytes()); //绝对路径
fileOutputStream.close();
MainActivity.handler.post(()->{
MainActivity.textView.setText("file over");
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
所以,现在发现想给手机编一个程序,自由上传下载图片到手机的图库是不可能的,因为android的权限太多太大。自编程序只有在自己的包内操作的权限。所以想到手机root解锁,是不是就是把全手机的文件都改成可以读写?
注意用openFileOutput openFileInput 的路径名是相对路径,不能用绝对路径,否则报错。
android 文件类 FileOutputStream FileInputStream 用绝对路径
openFileOutput openFileInput 用相对路径
文件存储只能在这两个目录下进行
data/data/包名 //不要读取权限
sdcard目录 //要读取权限
openFile* 两个类的权限:
MODE_PRIVATE //默认:私有
MODE_APPEND //在文件中追加
MODE_WORLD_READABLE //其他程序可以读
MODE_WORLD_WRITEABLE //其他程序可以写
android 文件方法: //文件路径都是相对路径
openFileInput
openFileOutput
getFileStreamPath //取文件路径
deleteFile //删除
fileList // 列出本包files的文件
=======================================================
activity
直观理解,activity就是手机屏幕的一个窗口。
主activity
启动新的activity
package com.example.wz;
import static android.content.Context.MODE_PRIVATE;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
static Handler handler=new Handler();
static TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
EditText editText = findViewById(R.id.editTextText);
textView = findViewById(R.id.textView);
button.setOnClickListener(view -> {
Intent intent=new Intent(MainActivity.this,MainActivity2.class); //启动新的activity
startActivity(intent);
});
}
}
建立好子activity Java程序和xml配置文件,再用intent启动 intent第一个参数为按钮所在程序的程序名, 第二个参数为要启动的程序名。所以用intent 可以跳到任何窗口,包括从子窗口跳回主窗口。为什么要加.this 和.class , 可以是规定吧。
如果不怕复杂,可以把事件响应独立来写
View.OnClickListener listener=new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent(MainActivity2.this,MainActivity.class);
startActivity(intent);
}
};
button.setOnClickListener(listener);
intent 可以调用android系统的程序
下面的代码是调用android 的内置拨号程序,调用内置程序,不用自编activity。系统自动调出已固定的拨号activity。
package com.example.call;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=findViewById(R.id.button);
TextView textView=findViewById(R.id.editTextText); //输入电话号码
button.setOnClickListener(view -> {
String s=textView.getText().toString();
Intent intent=new Intent(Intent.ACTION_DIAL, Uri.parse("tel://"+s));
startActivity(intent); //利用intent 调用android内置拨号程序
});
}
}
到这里,就可以很好理解为什么在用某个不用钱的程序时,突然点出拚多多的程序。
activity 之间通过bundle互传数据
主窗口:
package com.example.wz;
import static android.content.Context.MODE_PRIVATE;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
@SuppressLint({"MissingInflatedId", "LocalSuppress"})
TextView textView=findViewById(R.id.textView);
button.setOnClickListener(view -> {
Bundle bundle=new Bundle();
String name="wz";
bundle.putString("name",name);
Intent intent=new Intent(MainActivity.this,MainActivity2.class);//切换到第二窗口
intent.putExtras(bundle);
startActivity(intent);
});
Intent intent=getIntent(); //提取第二窗口传递的数据
String name1=intent.getStringExtra("kk");
textView.setText(name1);
}
}
子窗口
package com.example.wz;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity2 extends AppCompatActivity {
String name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Intent intent=getIntent(); //提取主窗口传递的数据
name=intent.getStringExtra("name");
TextView textView=findViewById(R.id.textView2);
textView.setText(name);
@SuppressLint({"MissingInflatedId", "LocalSuppress"})
Button button=findViewById(R.id.button2);
button.setOnClickListener(view -> {
Intent intent1=new Intent(MainActivity2.this,MainActivity.class); //切换到主窗口
Bundle bundle=new Bundle(); //绑定的数据
bundle.putString("kk","wl");
intent1.putExtras(bundle);
startActivity(intent1);
});
}
}
用这个方法,不用调用带返回数据的startactivity方法。
android 系统内置了很多常用的activity。如打电话的activity,
——————————————————————————————————————————–
android 图片的接收,删除,显示,图片列表显示
图片存储有两种方式,一种是存于自身程序包内,一种是存于像册,下面的程序是处理存在程序包中的情况。
如程序都在一个窗口中显示,就不用考虑activity的问题
图片存储路径:/data/data/包名/
接收图片的手机与发送图片的电脑,必须在同一个局域网中
代码:
package com.example.call;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class MainActivity extends Activity {
TextView textView;
@SuppressLint("MissingInflatedId")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
button.setText("接收");
Button button2=findViewById(R.id.button2);
button2.setText("播放");
Button button3=findViewById(R.id.button14);
button3.setText("目录");
Button button4=findViewById(R.id.button3);
button4.setText("删除");
textView=findViewById(R.id.textView);
ImageView imageView = findViewById(R.id.imageView);
imageView.setImageResource(R.drawable.ic_launcher_background);
EditText editText=findViewById(R.id.editTextText);
button4.setOnClickListener(view -> {
String name=editText.getText().toString();
textView.setText(name);
deleteFile(name);
});
button3.setOnClickListener(view -> {
String[] s=fileList();
String o=null;
for(String k:s){
o=o+k+" ";
}
textView.setText(o);
});
button2.setOnClickListener(view -> {
String name=editText.getText().toString();
textView.setText(name);
Bitmap myBitmap = BitmapFactory.decodeFile("/data/data/com.example.call/files/"+name);
imageView.setImageBitmap(myBitmap);
});
button.setOnClickListener(view -> {
try {
Socket socket=new Socket("192.168.43.61",3000);
InputStream inputStream=socket.getInputStream();
ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
String name=objectInputStream.readUTF();
byte[] b= (byte[]) objectInputStream.readObject();
FileOutputStream fileOutputStream=openFileOutput(name,Context.MODE_PRIVATE);
fileOutputStream.write(b);
fileOutputStream.close();
socket.close();
} catch (IOException e) {
// throw new RuntimeException(e);
textView.setText("net 错误");
} catch (ClassNotFoundException e) {
// throw new RuntimeException(e);
}
});
}
}
上传图片
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
public static void main(String[] args) throws IOException {
String s="/home/wzpc/3.jpg";
File file=new File(s);
String name=file.getName();
FileInputStream fileInputStream=new FileInputStream(file);
byte[] b=new byte[fileInputStream.available()];
fileInputStream.read(b);
ServerSocket serverSocket=new ServerSocket(3000);
Socket socket=serverSocket.accept();
OutputStream outputStream=socket.getOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);
objectOutputStream.writeUTF(name);
objectOutputStream.flush();
objectOutputStream.writeObject(b);
objectOutputStream.flush();
socket.close();
fileInputStream.close();
}
}
实.物图
android 四个核心组件:Activity Service Content.Provider BroadcastReceiver
在 activity 主程序的onCreate(Bundel bundle)中,可以直接引用android 的Actitvty,Intent,Content三个类的全部方法。如Content类的openFileoutput(). 不用创造实例。可以这样写:this.方法名。
android.App.Activity 继承与android.Content.Context. 所以这两个类的方法都可以直接引用
onCreate(Bundle) 是初始化 Activity 的地方。 最重要的是,在这里您通常会使用定义 UI 的布局资源调用 setContentView(int),并使用 findViewById(int) 检索该 UI 中需要以编程方式交互的小部件
onPause() 是处理用户暂停与 Activity 的主动交互的地方。 此时应提交用户所做的任何更改(通常提交给保存数据的 ContentProvider)。 在此状态下,活动在屏幕上仍然可见。
要与 Context.startActivity() 一起使用,所有 Activity 类都必须在其包的 AndroidManifest.xml 中具有相应的 <activity> 声明。
现在理一下:view UI的各个组成部件,如按钮的外观,event 事件响应
activity 由多个view组*成activity窗口。
Intent 主要是切换activity窗口
Context.Provider 主要是用来保存数据
ContentProvider的作用是
为不同的应用之间数据共享,提供统一的接口
,我们知道安卓系统中应用内部的数据是对外隔离的,要想让其它应用能使用自己的数据(例如通讯录)这个时候就用到了ContentProvider。
android 数据库
查询数据库图片,音乐,视频
“_data” 在数据库中代表文件的全路径名,它是非常重要的列名。
下面程序的cursor 的数值就是查询后数据的集合,采用循环一行一行读取列(_data)的值。
package com.example.test1;
import static android.content.ContentValues.TAG;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@SuppressLint("Range")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView2);
ImageView imageView = findViewById(R.id.imageView);
ContentResolver contentResolver = getContentResolver(); //读数据库
Uri uri =MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //读外部存储数据库 _data 代表图片文件的全路径
// 音频MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
// 视频MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data"}, null, null, null, null); //查询
// int t = cursor.getColumnCount(); //数据库 列数量
// String[] columnNames = cursor.getColumnNames(); //数据库全部 列名字 如query()方法的第二参数已限制列名,则columnnames=第二参数名,现在就是_data
// int t1 = cursor.getColumnIndex("_data"); //返回给定 列名称的从零开始的索引,如果该列不存在,则返回-1。
// int t2 = cursor.getCount(); //数据库的总行数;
while (cursor.moveToNext()) { // 移到数据库的下一行。
/* for (String Name : columnNames) { //name数据库的某一列,columnnames 全部列名
Log.d(TAG, "----------------------------");
Log.d(TAG, "name--> " + Name + " value--> " + cursor.getString(cursor.getColumnIndex(Name)));
Log.d(TAG, "----------------------------");
}*/
//因为只查询了一列"_data",可以这样简写
Log.d(TAG, "----------------------------"); //在电脑的Run框中显示输出
Log.d(TAG, "name-->_data " + " value--> " + cursor.getString(cursor.getColumnIndex("_data"))); //显示数据库中图片的路径,根据索引反查索引值
Log.d(TAG, "----------------------------");
}
Bitmap bitmap = BitmapFactory.decodeFile("/storage/emulated/0/Pictures/Screenshots/Screenshot_20230701_101256_com.tencent.mobileqq.jpg"); //显示图片
imageView.setImageBitmap(bitmap);
}
}
run框显示
Uri:
Uri uri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
图片MediaStore.Images.Media.EXTERNAL_CONTENT_URI
音频MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
视频MediaStore.Video.Media.EXTERNAL_CONTENT_URI
查询文件名,和文件路径。文件名:_display_name 文件路径 : _data
Cursor cursor = contentResolver.query(uri,new String[]{"_display_name","_data"}, null, null, null, null); //查询
String[] s=cursor.getColumnNames();
String o=null;
for(String k:s){
o=o+k+" ";
}
Log.d(TAG, "----------------------------");
Log.d(TAG,o);
while (cursor.moveToNext()) { // 移到数据库的下一行。
Log.d(TAG,cursor.getString(cursor.getColumnIndex("_display_name")));
Log.d(TAG,cursor.getString(cursor.getColumnIndex("_data"))); //显示数据库中图片的路径,根据索引反查索引值
}
Log.d(TAG, "----------------------------");
查询数据库步骤:query()方法的第二个参数是字符串数组,它的元素就是数据库要查询的列名字,如_data ,_display_name。 query方法生成的Cursor 就是要查询的列的全部行的值。提取每行采用 moveToNext()方法,取出每行的值采用:先用列名提取列在每行中的索引(getColumnIndex(列名)),再用此索引号倒查此索引的值。
经验证,新产生的照片,如刚拍的照片,会立即填加在数据库的末端。用这个方法,可以提取出刚拍的照片。
cursor.moveToLast(); //跳到数据库最后一行 Log.d(TAG,cursor.getString(cursor.getColumnIndex("_data")));//显示最后一行的data指向的最近写入数据库的照片。
浏览手机全部照片
package com.example.test1;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
int n=0;
@SuppressLint("Range")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView2);
ImageView imageView = findViewById(R.id.imageView);
Button button=findViewById(R.id.button3);
ContentResolver contentResolver = getContentResolver(); //读数据库
Uri uri =MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //读外部存储数据库 _data 代表图片文件的全路径
// 音频MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
// 视频MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data"}, null, null, null, null); //查询
ArrayList<String> al=new ArrayList<>();
while (cursor.moveToNext()) { // 移到数据库的下一行。
String s=cursor.getString(cursor.getColumnIndex("_data"));
al.add(s); //把数据库全部的图片路径存入ArrayList中
}
// textView.setText(String.valueOf(al.size()));
button.setOnClickListener(view -> { //按钮按一下,浏览一张图片
n++;
Bitmap bitmap=BitmapFactory.decodeFile(al.get(n));
imageView.setImageBitmap(bitmap);
});
}
}
感觉这个小程序的图片浏览加载速度快与手机的图片浏览app。
自动播放手机数据库图片
此程序相当重要,涉及到线程之间的数据交换,ui的定时更新
package com.example.test1;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.MediaStore;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
int n=0;
@SuppressLint("Range")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView2);
ImageView imageView = findViewById(R.id.imageView);
Button button=findViewById(R.id.button3);
ContentResolver contentResolver = getContentResolver(); //读数据库
Uri uri =MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //读外部存储数据库 _data 代表图片文件的全路径
// 音频MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
// 视频MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data"}, null, null, null, null); //查询
ArrayList<String> al=new ArrayList<>();
while (cursor.moveToNext()) { // 移到数据库的下一行。
String s = cursor.getString(cursor.getColumnIndex("_data"));
al.add(s); //把数据库全部的图片路径存入ArrayList
}
Handler.Callback hc=new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) { //这里必须调用handle类的镶嵌类Handle.Callback的handleMessage方法。
int t=msg.arg1;
Bitmap bitmap = BitmapFactory.decodeFile(al.get(t));
imageView.setImageBitmap(bitmap);
return false;
}
};
Handler handler =new Handler(Looper.myLooper(),hc);
new Thread(()->{
while(true) {
Message message = new Message();
message.arg1=n;
handler.sendMessage(message);
n++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// throw new RuntimeException(e);
}
}
}).start();
}
}
此程序可以作为ui循环或定时更新的模板。
编程发现:android 主方法线程中,不能用for循环等方法为ui的各种显示控件赋值来完成ui的更新
必须采用上述hendle类来完成。
为什么要这样,我理解是因为android的主线程不能有占长时间运行的代码。如果必须有这种长耗时代码,必须把这种代码移到一新线程中。再把数据通过message传回主线程
android 读取数据库的权限问题
华为畅享20 ART-AL00m 用的是android 10 ,除了在全局文件中加入权限外,还要在
application段中加入这句 android:requestLegacyExternalStorage="true" 如果不在程序中加入动态权限,那么一定要在手机的设置中开启你的程序的存储权限。
华为nova8 不用加这句可运行。加了也不影响。还有如果发现程序不运行,多是手机的存储权限关闭,在手机设置中开启就可以了。nova8 很少有关闭权限的情况,畅享20经常发生。
android 数据库音频
以上介绍了几种播放方式,可以说各有优劣。
本文写的不够详细,只是大概的介绍一下几种播放方式。在此做下简单的总结。
对于延迟度要求不高,并且希望能够更全面的控制音乐的播放,MediaPlayer比较适合
声音短小,延迟度小,并且需要几种声音同时播放的场景,适合使用SoundPool
对于简单的播放,不需要复杂控制的播放,可以给使用AsyncPlayer,所有操作均在子线程不阻塞UI
播放大文件音乐,如WAV无损音频和PCM无压缩音频,可使用更底层的播放方式AudioTrack。它支持流式播放,可以读取(可来自本地和网络)音频流,却播放延迟较小。
ps:据我测试AudioTrack直接支持WAV和PCM,其他音频需要解码成PCM格式才能播放。(其他无损格式没有尝试,有兴趣可以使本文提供的例子测试一下)
.jet的音频比较少见(有的游戏中在使用),可使用专门的播放器JetPlayer播放
对于系统类声音的播放和操作,Ringtone更适合(主要是掌握好RingtoneManager)
————————————————
版权声明:本文为CSDN博主「一个有梦想的屌丝程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013366008/article/details/76577372
package com.example.wz;
import android.annotation.SuppressLint;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
MediaPlayer mediaPlayer; //必须onCreate()方法前先定义MediaPlayer,否则华为手机只能播放音乐1-2秒就停止,不知道为啥
@SuppressLint({"MissingInflatedId", "Range"})
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Uri u=Uri.parse("/storage/emulated/0/qqmusic/song/阳山伟伟 - 星辰 (Original Mix) [mqms2].flac");
mediaPlayer = MediaPlayer.create(this.getApplicationContext(),u);
mediaPlayer.start();
}
}
必须onCreate()方法前先定义MediaPlayer,否则华为手机只能播放音乐1-2秒就停止,不知道为啥,这个解决方法是在网络发现的。其他手机没有试过。
播放音频要在全局文件中加入这两种权限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> 还有一点,因为所以程序都没有写动态权限,所以必须要在手机的设置中为程序赋予读写存储的权限 下面是android官网使用的代码,最好用这种代码,因为上面的三行代码在畅享20中不能运行。
主线程使用这种prepare()方式为同步播放,影响ui的更新,最好另起一子线程
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
另起一子线程播放
new Thread(()->{
Uri u=Uri.parse(al.get(2));
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(getApplicationContext(),u);
mediaPlayer.prepare(); //同步播发,影响ui的更新,如采用同步播放音乐,最好另开一子线程
mediaPlayer.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}).start();
主线程异步播放
官网代码
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final String ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mediaPlayer = ... // initialize it here
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}
主线程异步播放实用代码
Uri u=Uri.parse(al.get(1));
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(this.getApplicationContext(),u);
mediaPlayer.prepareAsync(); //这句必须放在setDataSource的后面,否则报错
} catch (IOException e) {
throw new RuntimeException(e);
}
mediaPlayer.setOnPreparedListener(mediaPlayer1 -> { //音乐异步准备完成播放,为什么是mediaPlay1,不清楚,程序自动显示的
mediaPlayer.start();
});
顺序播放数据库所有音乐,此程序相当重要,主要理解监控音乐是否完成的线程
package com.example.test1;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.IOException;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
static int is=0; //音乐播放完成标志位
MediaPlayer[] mp;
MediaPlayer mediaPlayer;
int n=0;
@SuppressLint("Range")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView2);
ImageView imageView = findViewById(R.id.imageView);
Button button=findViewById(R.id.button3);
ContentResolver contentResolver = getContentResolver(); //读数据库
// Uri uri =MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //读外部存储数据库 _data 代表图片文件的全路径
Uri uri=MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
// 视频MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data"}, null, null, null, null); //查询
ArrayList<String> al=new ArrayList<>();
while (cursor.moveToNext()) { // 移到数据库的下一行。
String s = cursor.getString(cursor.getColumnIndex("_data"));
al.add(s); //把数据库全部的图片路径存入ArrayList
}
class Th extends Thread{
public void run() {
is=0; //播放开始 清标志位
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(al.get(n));
mediaPlayer.prepareAsync(); //这句必须放在setDataSource的后面,否则报错
} catch (IOException e) {
throw new RuntimeException(e);
}
mediaPlayer.setOnPreparedListener(mediaPlayer1 -> { //音乐异步准备完成播放,为什么是mediaPlay1,不清楚,程序自动显示的
mediaPlayer.start();
});
mediaPlayer.setOnCompletionListener(mediaPlayer1 -> {
is=1; //播放完成置标志位
});
}
}
button.setOnClickListener(view ->{ //按键启动播放音乐
Th th=new Th();
th.start();
});
//------重点---------------------------------------------------------
new Thread(()->{ //新开一线程,一直监控标志位is
while(true) { //is=1,表示音乐播放完成,播放线程已关闭
if (is == 1) {
n++; //跳到下一首音乐路径
Th th = new Th(); //新开一播放音乐线程
th.start();
}
try {
Thread.sleep(2000); //防线程误启动时间,也是两首音乐的间隔时间
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
}
android 视频
在
Android
中,我们有三种方式来实现视频的播放:
1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。
3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。
播放数据库视频
自动顺序播放手机全部视频
package com.example.test1;
import static android.content.ContentValues.TAG;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Button;
import java.io.IOException;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
SurfaceHolder surfaceHolder;
int n=0;
static int is=0;
MediaPlayer mediaPlayer;
@SuppressLint("Range")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@SuppressLint({"WrongViewCast", "MissingInflatedId", "LocalSuppress"})
SurfaceView surfaceView=findViewById(R.id.surfaceView);
Button button=findViewById(R.id.button3);
ContentResolver contentResolver = getContentResolver(); //读数据库
// Uri uri =MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //读外部存储数据库 _data 代表图片文件的全路径
// Uri uri=MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri uri=MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data"}, null, null, null, null); //查询
ArrayList<String> al=new ArrayList<>();
while (cursor.moveToNext()) { // 移到数据库的下一行。
String s = cursor.getString(cursor.getColumnIndex("_data"));
al.add(s); //把数据库全部的图片路径存入ArrayList
Log.d(TAG,s);
}
Runnable r=()->{
is=0;
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(al.get(n));
mediaPlayer.prepare();
} catch (IOException e) {
throw new RuntimeException(e);
}
surfaceHolder = surfaceView.getHolder(); //播放视频增加这两句
mediaPlayer.setDisplay(surfaceHolder); //播放视频增加这两句
mediaPlayer.start();
mediaPlayer.setOnCompletionListener(mediaPlayer1 -> {
is=1;
mediaPlayer.release(); //释放,关闭mediaPlay
});
};
button.setOnClickListener(view -> { //启动开始播放
Thread thread=new Thread(r);
thread.start();
});
new Thread(()->{
while(true){
if(is==1){
n++;
Thread thread=new Thread(r);
thread.start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
}
新开子窗口用于显示视频,activity 之间传递文件路径名
主窗口代码
package com.example.test1;
import static android.content.ContentValues.TAG;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Button;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
SurfaceHolder surfaceHolder;
int n=0;
static int is=0;
MediaPlayer mediaPlayer;
@SuppressLint("Range")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@SuppressLint({"WrongViewCast", "MissingInflatedId", "LocalSuppress"})
SurfaceView surfaceView=findViewById(R.id.surfaceView);
Button button=findViewById(R.id.button3);
ContentResolver contentResolver = getContentResolver(); //读数据库
// Uri uri =MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //读外部存储数据库 _data 代表图片文件的全路径
// Uri uri=MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri uri=MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data"}, null, null, null, null); //查询
ArrayList<String> al=new ArrayList<>();
while (cursor.moveToNext()) { // 移到数据库的下一行。
String s = cursor.getString(cursor.getColumnIndex("_data"));
al.add(s); //把数据库全部的图片路径存入ArrayList
Log.d(TAG,s);
}
button.setOnClickListener(view -> {
n++;
Bundle bundle=new Bundle();
String path=al.get(n); //为子窗口传递显示文件路径
bundle.putString("lj",path);
Intent intent=new Intent(MainActivity.this,Sp.class);
intent.putExtras(bundle);
startActivity(intent);
});
}
}
子窗口代码
package com.example.test1;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
public class Sp extends AppCompatActivity {
SurfaceHolder surfaceHolder;
MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sp);
@SuppressLint({"MissingInflatedId", "LocalSuppress"})
SurfaceView surfaceView=findViewById(R.id.surfaceView2);
Intent intent=getIntent();
String path=intent.getStringExtra("lj"); //取得主窗口传递的路径
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(path);
mediaPlayer.prepareAsync();
} catch (IOException e) {
throw new RuntimeException(e);
}
surfaceHolder = surfaceView.getHolder(); //播放视频增加这两句
surfaceHolder.addCallback(new SurfaceHolder.Callback() { //最好用callback此方法显示,否则太多报错
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
mediaPlayer.setDisplay(surfaceHolder); //播放视频增加这两句
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
}
});
mediaPlayer.setOnPreparedListener(mediaPlayer1 -> {
mediaPlayer.start();
});
mediaPlayer.setOnCompletionListener(mediaPlayer1 -> {
mediaPlayer.release();
});
}
//-------------------返回关闭视频---------------------------------------
public void onBackPressed() { //监听返回键,如返回则关闭视频
super.onBackPressed();
mediaPlayer.release();
}
}
顺序播放或者按文件序号点播视频
package com.example.test1;
import static android.content.ContentValues.TAG;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
SurfaceHolder surfaceHolder;
String outpath;
String outname;
String xs;
int n=0;
int no=0;
MediaPlayer mediaPlayer;
TextView textView;
EditText editText;
ArrayList<String> al;
Bundle bundle=new Bundle();
@SuppressLint("Range")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=findViewById(R.id.button3);
editText=findViewById(R.id.editTextText);
editText.setText("");
textView=findViewById(R.id.textView2);
textView.setText("");
ContentResolver contentResolver = getContentResolver(); //读数据库
// Uri uri =MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //读外部存储数据库 _data 代表图片文件的全路径
// Uri uri=MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri uri=MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data","_display_name"}, null, null, null, null); //查询
al=new ArrayList<>();
Map<Integer,String> map=new HashMap<>();
while (cursor.moveToNext()) { // 移到数据库的下一行。
String s = cursor.getString(cursor.getColumnIndex("_data"));
String name=cursor.getString(cursor.getColumnIndex("_display_name"));
al.add(s);
outname=outname+name+" ";
xs=xs+String.valueOf(no)+": "+name+" ";
map.put(no,s);
no++;
}
textView.setText(xs);
//----------按键启动顺序播放-----------------------------------------
button.setOnClickListener(view -> {
n++;
// Bundle bundle=new Bundle();
String path=al.get(n);
bundle.putString("lj",path);
Intent intent=new Intent(MainActivity.this,Sp.class);
intent.putExtras(bundle);
startActivity(intent);
});
//-----------按键监听,实现输入文件序号单个播放---------------------------------------------------------------
editText.setOnKeyListener(new View.OnKeyListener() { //正对输入框editText的按键监听
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER: //监控 按下回车键
String in= editText.getText().toString();
int index=Integer.valueOf(in);
String path= map.get(index);
bundle.putString("lj",path);
Intent intent=new Intent(MainActivity.this,Sp.class);
intent.putExtras(bundle);
startActivity(intent);
return true;
}
}
return false;
}
});
}
}
子窗口代码不变
使用发现,利用新线程监控播放线程不稳定
改进后的顺序音乐播放
package com.example.test1;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.util.ArrayList;
public class Yy extends AppCompatActivity {
TextView textView;
TextView textView1;
EditText editText;
Button button1;
Button button2;
Button button3;
Button button4;
int n = 0;
int is = 0;
ArrayList<String> al = new ArrayList<>();
MediaPlayer mediaPlayer = new MediaPlayer();
@SuppressLint({"MissingInflatedId", "Range"})
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_yy);
textView = findViewById(R.id.textView2);
textView1 = findViewById(R.id.textView3);
textView1.setTextSize(24);
editText = findViewById(R.id.editTextText);
editText.setText("");
button1 = findViewById(R.id.button6);
button1.setText("顺序播放");
button2 = findViewById(R.id.button7);
button2.setText("停止选播");
button3 = findViewById(R.id.button8);
button3.setText("进一首");
button4 = findViewById(R.id.button9);
button4.setText("退一首");
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, new String[]{"_data"}, null, null, null, null);
String o = null;
while (cursor.moveToNext()) {
@SuppressLint("Range")
String s = cursor.getString(cursor.getColumnIndex("_data"));
al.add(s);
o = o + s + " ";
}
textView.setText(o);
textView1.setText("音乐序号"+String.valueOf(cursor.getCount()));
//------顺序全部播放--------------------------------------------------------
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Handler.Callback hc = new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
int t=message.arg1;
textView1.setText("音乐序号:"+String.valueOf(t));
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(al.get(t));
mediaPlayer.prepareAsync(); //这句必须放在setDataSource的后面,否则报错
} catch (IOException e) {
throw new RuntimeException(e);
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
return false;
}
};
Handler handler=new Handler(Looper.getMainLooper(),hc);
new Thread(()->{
Message message = new Message();
message.arg1 = n;
handler.sendMessage(message);
while(true){
if(mediaPlayer!=null) {
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
n++;
Message message = new Message();
message.arg1 = n;
handler.sendMessage(message);
}
});
}
}
}).start();
}
});
//---------暂停音乐播放,并选播,填如要开始播放的序号------------------------
button2.setOnClickListener(view -> {
if(mediaPlayer.isPlaying()){
mediaPlayer.release();
}
String in=editText.getText().toString();
n=Integer.valueOf(in);
});
//----------进一首开始顺序播放---------------------------------------------
button3.setOnClickListener(view -> {
if(mediaPlayer.isPlaying()){
mediaPlayer.release();
}
Handler.Callback hc = new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
int t=message.arg1;
textView1.setText("音乐序号:"+String.valueOf(t));
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(al.get(t));
mediaPlayer.prepareAsync(); //这句必须放在setDataSource的后面,否则报错
} catch (IOException e) {
throw new RuntimeException(e);
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
return false;
}
};
Handler handler=new Handler(Looper.getMainLooper(),hc);
new Thread(()->{
n=n+1;
Message message = new Message();
message.arg1 = n;
handler.sendMessage(message);
while(true){
if(mediaPlayer!=null) {
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
n++;
Message message = new Message();
message.arg1 = n;
handler.sendMessage(message);
n++;
}
});
}
}
}).start();
});
//-----------退一首开始顺序播放--------------------------------------------------
button4.setOnClickListener(view -> {
if(mediaPlayer.isPlaying()){
mediaPlayer.release();
}
Handler.Callback hc = new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
int t=message.arg1;
textView1.setText("音乐序号:"+String.valueOf(t));
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(al.get(t));
mediaPlayer.prepareAsync(); //这句必须放在setDataSource的后面,否则报错
} catch (IOException e) {
throw new RuntimeException(e);
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
return false;
}
};
Handler handler=new Handler(Looper.getMainLooper(),hc);
new Thread(()->{
n=n-1;
Message message = new Message();
message.arg1 = n;
handler.sendMessage(message);
while(true){
if(mediaPlayer!=null) {
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
n++;
Message message = new Message();
message.arg1 = n;
handler.sendMessage(message);
n++;
}
});
}
}
}).start();
});
//-=====================================================================================
}
@Override
public void onBackPressed () {
super.onBackPressed();
mediaPlayer.release();
mediaPlayer = null;
}
}
android 按键事件响应
返回键响应
//-------------------返回关闭视频---------------------------------------
public void onBackPressed() { //监听返回键,如返回则关闭视频
super.onBackPressed();
mediaPlayer.release();
}
文本输入框的按键响应
editText.setOnKeyListener(new View.OnKeyListener() { //针对文本输入框的按键事件
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if(keyEvent.getAction()==KeyEvent.ACTION_DOWN){ //当按键按下
if(i==KeyEvent.KEYCODE_ENTER){ //当键值为回车键
textView.setText("ok");
}
}
return false;
}
});