博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
高仿微信新消息提示音功能
阅读量:7057 次
发布时间:2019-06-28

本文共 13479 字,大约阅读时间需要 44 分钟。

近期公司在做一个项目。有一个切换消息提示音的功能,能够切换本应用收到消息的提示音,而不影响系统提示音。我就依照微信的那个样式进行了编程,终于得到想要的效果。

转载请注明出处。谢谢:

怕有些人不知道怎么进入微信的新消息提示音功能,我这里说下操作步骤:

打开微信----我---设置---新消息提醒---新消息提示音。

经过以上的步骤就进入了这种界面

这个是微信的效果图。

以下是我自己编程的效果图,例如以下图所看到的:

能够看到这两效果区别不是非常大。

如今開始介绍一下详细实现的步骤。

本功能的最基本的功能是,这也是难点之中的一个:获取到手机系统的提示音,并将它们显示在一个listview里面。

參考例如以下代码:

// 获得RingtoneManager对象		RingtoneManager manager = new RingtoneManager(this);		// 设置RingtoneManager对象的类型为TYPE_NOTIFICATION。这样仅仅会获取到notification的相应内容		manager.setType(RingtoneManager.TYPE_NOTIFICATION);		Cursor cursor = manager.getCursor();		int num = cursor.getCount();		Log.i("tag", num + "消息音个数");		// 存储消息音名字的arrayList		ArrayList
ringtoneList = new ArrayList
(); for (int i = 0; i < num; i++) { //获取当前i的铃声信息 Ringtone ringtone = manager.getRingtone(i); //获取当前i的uri,设置notification的自己定义铃声要用到 Uri uri = manager.getRingtoneUri(i); //获取到当前铃声的名字 String title = ringtone.getTitle(this); ringtoneList.add(title); }
将获取到的消息提示音的名字。增加到arrayList里。

先将主界面的信息贴上来。看一下,我再慢慢解释:

package jz.his.activity;import java.util.ArrayList;import jz.his.adapter.RingtoneAdapter;import jz.his.jzhis.R;import jz.his.util.SharedPreferenceUtil;import android.app.Activity;import android.content.Intent;import android.database.Cursor;import android.media.Ringtone;import android.media.RingtoneManager;import android.net.Uri;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.Window;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ListView;public class RingtoneActivity extends Activity {	ArrayList
ringtoneList; ListView listView; RingtoneManager manager; RingtoneAdapter adapter; String ringName = ""; /** * 选择铃声的uri */ Uri uri = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_ringtone); listView = (ListView) findViewById(R.id.ringtone); getRingtone(); // initRingtoneManager(); // ringtoneList = FunctionActivity.ringtoneList; adapter = new RingtoneAdapter(this, ringtoneList, getIndex()); listView.setAdapter(adapter); // 设置从第getIndex()行開始显示 listView.setSelection(getIndex()); listView.setOnItemClickListener(new OnItemClickListener() { @SuppressWarnings("static-access") @Override public void onItemClick(AdapterView
parent, View view, int position, long id) { // 当点击的item是第一个“尾随系统”时 if (position == 0) { // 得到系统默认的消息uri Uri defalutUri = manager .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); // 通过URI获得系统默认的Ringtone发出声音 Ringtone defalutRingtone = manager.getRingtone( RingtoneActivity.this, defalutUri); defalutRingtone.play(); ringName = "尾随系统"; uri = null; } else { // 当点击的item不是第一个“尾随系统”时,获得的铃声要减一才对 Ringtone ringtone = manager.getRingtone(position - 1); uri = manager.getRingtoneUri(position - 1); ringtone.play(); ringName = ringtone.getTitle(RingtoneActivity.this); } adapter.first = new int[ringtoneList.size()]; if (adapter.first[position] == 0) { adapter.first[position] = 1; } else { adapter.first[position] = 0; } adapter.notifyDataSetChanged(); } }); } /** * 初始化RingtoneManager对象,在listview的点击事件里面。用到了 */ private void initRingtoneManager() { manager = new RingtoneManager(this); manager.setType(RingtoneManager.TYPE_NOTIFICATION); manager.getCursor(); } /** * 得到当前铃声的行数 */ private int getIndex() { for (int i = 0; i < ringtoneList.size(); i++) { if (SharedPreferenceUtil.getString(RingtoneActivity.this, SharedPreferenceUtil.RINGTONE_NAME).equals( ringtoneList.get(i))) { return i; } } return 0; } /** * 得到ringtone中的全部消息声音 */ private void getRingtone() { manager = new RingtoneManager(this); manager.setType(RingtoneManager.TYPE_NOTIFICATION); Cursor cursor = manager.getCursor(); int num = cursor.getCount(); Log.i("tag", num + "消息音个数"); ringtoneList = new ArrayList
(); for (int i = -1; i < num; i++) { if (i == -1) { ringtoneList.add("尾随系统"); } else { Ringtone ringtone = manager.getRingtone(i); // Uri uri = manager.getRingtoneUri(i); String title = ringtone.getTitle(this); ringtoneList.add(title); } } } public void allClick(View v) { switch (v.getId()) { case R.id.back_button: finish(); break; case R.id.save: if (ringName == "") { // 没有修改铃声直接关闭界面 finish(); } else { // 已经修改uri。假设又选择了尾随系统,则uri为null,其它的就是uri本身 if (uri == null) { SharedPreferenceUtil.setString(RingtoneActivity.this, SharedPreferenceUtil.url_string, ""); } else { SharedPreferenceUtil.setString(RingtoneActivity.this, SharedPreferenceUtil.url_string, uri.toString()); } Intent intent = new Intent(); intent.putExtra("ringName", ringName); intent.setClass(RingtoneActivity.this, FunctionActivity.class); startActivity(intent); } default: break; } }}

解释1. 

由于listView显示的第一行是一个“追随系统”的item。所以我在适配数据的时候。有些小改变。在i=-1的时候,将ringtoneList加入为“追随系统”,其它的不变。由于进行了这种处理,那么在点击各个item时候。获得铃声并进行播放时候。要做这种处理:

Ringtone ringtone = manager.getRingtone(position - 1);

解释2.

终于将选择的铃声uri路径以String的格式存入到sharedPreference中。

在service里面进行设置,例如以下所看到的:主要看15--24行。

NotificationManager notificationManager = (NotificationManager) this				.getSystemService(Context.NOTIFICATION_SERVICE);		Notification n = new Notification();		Intent intent = new Intent(this, FunctionActivity.class);		intent.putExtra("messageData","messageData" );		PendingIntent pi = PendingIntent.getActivity(this, 0, intent,				PendingIntent.FLAG_ONE_SHOT);		n.contentIntent = pi;		// n.defaults = Notification.DEFAULT_ALL;		if (SharedPreferenceUtil				.getBoolean(this, SharedPreferenceUtil.IS_SOUND)) {		} else {			// 假设消息声音开启			if (!SharedPreferenceUtil.getStringNull(OnlineService.this,					SharedPreferenceUtil.url_string).equals("")) {				// 假设选择了其它的系统声音				n.sound = Uri.parse(SharedPreferenceUtil.getString(						OnlineService.this, SharedPreferenceUtil.url_string));			} else {				// 默认的系统声音				n.defaults |= Notification.DEFAULT_SOUND;			}		}		if (SharedPreferenceUtil.getBoolean(this,				SharedPreferenceUtil.IS_VIBRATE)) {		} else {			n.defaults |= Notification.DEFAULT_VIBRATE;		}		n.flags |= Notification.FLAG_SHOW_LIGHTS;		n.flags |= Notification.FLAG_AUTO_CANCEL;		// n.sound=Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");		n.icon = R.drawable.ic_launcher;		n.when = System.currentTimeMillis();		n.tickerText = tickerText;		n.setLatestEventInfo(this, title, content, pi);		notificationManager.notify(id, n);
注意:假设是要选择其它的声音,直接是n.sound = 其它声音的Uri

这个真的很重要,就直接这样就能够了,看网上一大堆什么

notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6"); //使用系统提供的铃音
并不能有效果。我也不清楚为什么。假设大家有合理的解释,请告知。嘿嘿。

如今谷歌官方已经不推荐上面的那种notification的做法了,新的做法是以下的这个:

Bitmap btm = BitmapFactory.decodeResource(getResources(),				R.drawable.ic_launcher);		// 这里大图标。小图标刚好相反		NotificationCompat.Builder builder = new NotificationCompat.Builder(				this).setSmallIcon(R.drawable.ic_launcher)				.setContentTitle(title).setContentText(content)				.setTicker(tickerText);		if (SharedPreferenceUtil				.getBoolean(this, SharedPreferenceUtil.IS_SOUND)) {		} else {			// 假设消息声音开启			if (!SharedPreferenceUtil.getStringNull(OnlineService.this,					SharedPreferenceUtil.url_string).equals("")) {				// 假设选择了其它的系统声音				builder.setSound(Uri.parse(SharedPreferenceUtil.getString(						OnlineService.this, SharedPreferenceUtil.url_string)));			} else {				// 默认的系统声音				builder.setDefaults(Notification.DEFAULT_SOUND);			}		}				if (SharedPreferenceUtil.getBoolean(this,				SharedPreferenceUtil.IS_VIBRATE)) {		} else {			builder.setDefaults(Notification.DEFAULT_VIBRATE);		}		// 构建一个Intent		Intent intent = new Intent(this, FunctionActivity.class);			intent.putExtra("messageData","messageData" );		sendData();		// 封装一个Intent		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,				intent, PendingIntent.FLAG_ONE_SHOT);		// 设置通知主题的意图		builder.setContentIntent(pendingIntent);		// 获取通知管理器对象		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);		notificationManager.notify(id, builder.build());
关于这个新的notification的使用方法,你能够參看这个文章:

只是这个有个问题就是,builder.setDefaults()这种方法设置默认的铃声。闪关灯,震动,每次仅仅能设置一次,不能多次调用这种方法来设置。假设有大神知道。请告知我。

解释3:

当点击保存button后。就进入到之前的界面,由于我之前的界面是一个viewpager+fragment的一个界面。一个activity里面增加了四个Fragment的这种一个界面。进入到主activity时候,进行推断:

/**	 * 选择消息提示音后,跳转到功能界面后。直接将其跳转设置界面	 */	private void selectRingtone() {		String ringName = getIntent().getStringExtra("ringName");		Log.e("tag", ringName+"传过来的值");		if (ringName != null) {			pager.setCurrentItem(2);		}	}
直接跳转到第二个Fragment界面。例如以下图所看到的:

然后将设置界面的新消息提示音的内容进行改变:

newSound = (TextView) getActivity().findViewById(R.id.new_sounde_text);		newSound.setText(SharedPreferenceUtil.getStringSystem(getActivity(),				SharedPreferenceUtil.RINGTONE_NAME));		//第一次进入这个页面,以下的方法是不会运行的,由于ringName是null		String ringName = getActivity().getIntent().getStringExtra("ringName");		if (ringName != null) {			newSound.setText(ringName);			Log.e("tag", ringName+"要保存的值");			SharedPreferenceUtil.setString(getActivity(),					SharedPreferenceUtil.RINGTONE_NAME, ringName);		}

解释4:

你可能注意到。我在RingtoneActivity代码里凝视了两行代码,你会发现,当我们每次进入这个RingtoneActivity的时候。都要又一次载入数据到arrayList里面,尽管数据不是非常多,可是肉眼能够感觉到是有点卡顿的。那么为了防止卡顿,我在进入RingtoneActivity之前的FunctionActivity页面开了一个子线程先载入数据到arrayList里面。定义成static类型。例如以下所看到的:

static ArrayList
ringtoneList; Runnable run = new Runnable() { @Override public void run() { RingtoneManager manager = new RingtoneManager(FunctionActivity.this); manager.setType(RingtoneManager.TYPE_NOTIFICATION); Cursor cursor = manager.getCursor(); int num = cursor.getCount(); Log.i("tag", num + "消息音个数"); ringtoneList = new ArrayList
(); for (int i = -1; i < num; i++) { if (i == -1) { ringtoneList.add("尾随系统"); } else { Ringtone ringtone = manager.getRingtone(i); // Uri uri = manager.getRingtoneUri(i); String title = ringtone.getTitle(FunctionActivity.this); ringtoneList.add(title); } } } };

这样在ringtoneActivity中,通过
ringtoneList = FunctionActivity.ringtoneList;
就获得了数据,这样就不会有卡顿了。
可是我这种方法用到了static定义变量,这样easy造成oom,详细參看   
所以我弃用了这种方法,不知道有没有大神能够给个建议呢。

解释5:

当你选择了其它的铃声的时候,再次进入新消息提示音界面时候,是从当前选择的铃声開始展示的,如图所看到的:

当我选择了Clever这个铃声的时候,我再次进入这个页面。会从Clever这行開始显示。这个功能是这样实现的。嘿嘿,并没有想象的那么难吧,事实上listview就自带这种方法的,仅仅须要传入当前item的位置是第几个即可了。

// 设置从第getIndex()行開始显示		listView.setSelection(getIndex());

/**	 * 得到当前铃声的行数	 */	private int getIndex() {		for (int i = 0; i < ringtoneList.size(); i++) {			if (SharedPreferenceUtil.getString(RingtoneActivity.this,					SharedPreferenceUtil.RINGTONE_NAME).equals(					ringtoneList.get(i))) {				return i;			}		}		return 0;	}

RingtoneAdapter里的内容是这种:

package jz.his.adapter;import java.util.ArrayList;import jz.his.adapter.MessageAdapter.ViewHolder;import jz.his.jzhis.R;import android.content.Context;import android.opengl.Visibility;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;public class RingtoneAdapter extends BaseAdapter {	Context context;	ArrayList
list; /** * 建立一个数组。默认都为0 */ public int[] first; int index; public RingtoneAdapter(Context cont, ArrayList
arayList, int index) { context = cont; list = arayList; this.index = index; first = new int[list.size()]; } class ViewHolder { TextView title; ImageView image; } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(context).inflate( R.layout.item_ringtone, null); holder.title = (TextView) convertView.findViewById(R.id.title); holder.image = (ImageView) convertView.findViewById(R.id.image); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.title.setText(list.get(position)); if (first[position] == 0) { holder.image.setVisibility(View.GONE); } else { holder.image.setVisibility(View.VISIBLE); // 当点击其它item后。将index置为-1,则以下的if语句则不会再运行进入了 index = -1; } // 第一次进来的时候,让当前item的图片可见 if (position == index) { holder.image.setVisibility(View.VISIBLE); } return convertView; }}
RingtoneAdapter里进行了推断。是否某个item后面的对勾显示出来。这里当时还是纠结了一会的,终于还是攻克了。特此记录。

item_ringtone布局的代码例如以下所看到的:

里面的有两个空白的textView是为了扩开每一个item的高度。为了让铃声名字看起来是在中间位置。

activity_ringtone以下的布局代码例如以下所看到的:

Button的account_backg代码是:

xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 内部颜色 --> <solid android:color="@color/account_green" /> <!-- 边缘线条颜色 --> <stroke android:width="1dp" android:color="@color/account_green" /> <!-- 圆角的幅度 --> <corners android:bottomLeftRadius="5dip" android:bottomRightRadius="5dip" android:topLeftRadius="5dip" android:topRightRadius="5dip" /> </shape>

这样主要的功能就写完了,由于是公司整个项目的,所以不能发源代码给大家。可是有不论什么意见或者问题。能够在评论和我沟通。嘿嘿,共同进步嘛。

你可能感兴趣的文章
[LeetCode] Pow(x, n) 二分搜索
查看>>
简记mysql中文乱码(插入变成??)的问题.
查看>>
C# 科学计数法转换成数字
查看>>
深入理解:java类加载器
查看>>
Android Studio使用Git版本控制github
查看>>
跳转到移动终端
查看>>
Azure Storage Client Library 重试策略建议
查看>>
元素分类
查看>>
页面获取当前时间
查看>>
select,poll,epoll
查看>>
关于“foreach循环”中遇到的几个问题总结
查看>>
重温JSP学习笔记--El函数库
查看>>
模板引擎ejs的include方法
查看>>
NOIP2003 传染病控制
查看>>
bzoj千题计划316:bzoj3173: [Tjoi2013]最长上升子序列(二分+树状数组)
查看>>
linux: 堆排序和快速排序的整理
查看>>
请求数据传入(SpringMVC)
查看>>
第七篇 PHP编码规范
查看>>
队列(queue)
查看>>
jsHint-静态代码检查工具eclipse中使用
查看>>