自学 android

  • Post author:
  • Post category:其他


试了两天,不管是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/包名/

16fb5910fde24230815fcfb9f3f9ae56.png

接收图片的手机与发送图片的电脑,必须在同一个局域网中

代码:

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();
}
}




实.物图

c45343bc8918424c93c681b82d7a7cd8.jpg

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框显示

7b32a9d608854b429111350bee0de706.png

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;
            }
        });



版权声明:本文为m0_59802969原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。