对于音乐播放器来说,可以体现很多种android开发所要运用的技术,比方说数据库的查询,自定义控件的开发,事件的处理,Activity的运用,service的开发,多媒体的运用,布局的运用,BaseAdapter的开发,网络应用等,是非常适合用来做为考察自己android学习的项目
一.自定义控件
android中的按钮并没有直接提供播放的三角式按钮,所以这里需要我们自己实现这部分按钮,要实现的按钮有播放/暂停,模式切换,前进,后退,即点击播放后会变成暂停键,模式切换可以从单曲循环,顺序播放,循环播放,随机播放的按钮中互相变化,具体实现过程如下,首先定义一个attrs.xml文件,在res/values文件夹下,定义各个按键的键值,之后会在自定义控件中使用
<resources>
<declare-styleable name="CustomAudioPlayButton">
<attr name="ButtonValue">
<enum name="start" value="0"/>
<enum name="forward" value="1"/>
<enum name="backward" value="2"/>
<enum name="query" value="3"/>
<enum name="playmode" value="4"/>
attr>
declare-styleable>
resources>
之后就需要在入口文件处注册这个数值,注册方法如下,在要使用自定义组件的xml文件中写入xmlns:custom=”http://schemas.android.com/apk/res/com.example.mp3site”其中com.example.mp3site为所处包名,xmlns:custom中的custom为自定义,代表使用数据时的关键字
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.mp3site"
接下来就是控件的实现了,可以继承VIew类来重载其中的方法进行自己绘制需要的控件,对于播放按钮来说有两种选择,一是在中间载入图片,另一种就是通过drawline自己绘制,这里采用后者进行描述,通过typedarray来获取自定义控件使用时所输入的数据,因为是按钮所以还需要设置clickable,而自定义控件并没有写好的点击事件,那么使用使用可以通过ontouch来实现,也就是触摸,所以还需要设置触摸事件,完整的过程如下
public playbutton(Context context, AttributeSet attrs) {
super(context, attrs);
//获取所定义的值
TypedArray typeArray=context.obtainStyledAttributes(attrs,R.styleable.CustomAudioPlayButton);
type=typeArray.getInt(R.styleable.CustomAudioPlayButton_ButtonValue, defaultType);
Log.i("player","start draw"+type);
typeArray.recycle();
InitView();
//设置为可点击
this.setClickable(true);
//设置触摸事件
this.setOnTouchListener(new onTouch());
// TODO Auto-generated constructor stub
}
之后就是绘制按钮的图像了了,这里以播放和暂停来作为例子,首先我们需要一个外框图案,这里选择使用方形来作为外框,之后就是播放和暂停的切换,所以需要一个代表播放还是暂定的标志位,所以绘制的方法如下:
public void drawstartButton(Canvas canvas,Boolean pressed){
//如果是true,画双竖线,false话右三角
float []point={(float) (0.3*width),(float)0.1*height,(float)0.9*height,(float) (0.6*width),(float) (0.5*height)};
Log.i("player","start draw start");
if(flag_startstop){
canvas.drawLine(point[0], point[1],point[0],point[2], pressedpaint);
canvas.drawLine(point[3], point[1],point[3],point[2], pressedpaint);
}else{
canvas.drawLine(point[0], point[1],point[0],point[2], unpresspaint);
canvas.drawLine(point[0], point[1],point[3],point[4], unpresspaint);
canvas.drawLine(point[0], point[2],point[3],point[4], unpresspaint);
}
}
画完按钮之后还需要对触摸事件的响应,在触摸后切换图像的标志位,来达到播放和暂停的互相切换的目的
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressed = true;
if(type==start){
if(flag_startstop){
flag_startstop=false;
}else{
flag_startstop=true;
}}else if(type==mode){
if(playmode<4){
playmode++;
}else{
playmode=1;
}
}
//刷新视图
invalidate();
break;
case MotionEvent.ACTION_UP:
pressed = false;
invalidate();
break;
}
return false;
}
接下来就是使用了,使用方法如下,包名后写上类名,custom:ButtoValue即为所传的值
<com.example.mp3site.playbutton
android:id="@+id/btnStart"
android:layout_width="40dp"
android:layout_height="64dp"
android:layout_marginRight="25dp"
android:layout_toRightOf="@+id/btnMode"
custom:ButtonValue="start" />
二.mediaplayer的使用
mediaplayer.stop()//停止播放
mediaplayer.pause()//暂停播放
mediaplayer.start()//开始播放
mediaplayer.prepare()//准备播放
这些就是基本使用方法,值得注意的是,在首次播放时,需要等待prepare结束,在调用start进行播放
播放函数如下:
public void playmedia(String Name){
TextView tv=(TextView)MainActivity.this.findViewById(R.id.MusicName);
tv.setText(Name);
mPlay=new MediaPlayer();
//将进度条置为0
mPbar.setProgress(0);
//如果在播放,就重置播放器
if(mPlay.isPlaying()){
mPlay.reset();
}
try{
//设置播放文件流
String datapath="/sdcard/"+Name+".mp3";
mPlay.setDataSource(datapath);
//缓冲
mPlay.setOnPreparedListener(new OnPreparedListener(){
@Override
public void onPrepared(MediaPlayer mPlay) {
// TODO Auto-generated method stub
//获取Mp3文件长度
int width=mPlay.getDuration();
mPbar.setMax(width/1000);
//开始播放
mPlay.start();
//单进程可能会阻塞
new Thread(new refresh(width,mPlay)).start();
}
});
mPlay.prepareAsync();
}
三,通过系统提供的considerprovider来查询音乐的具体信息,因为是系统自带的,所以和自定义provider不同并不需要注册,直接拿来用就行了,具体使用的过程之前的笔记已经有了,就不再赘述了,下面就是调用过程MediaStore是系统自带的,非自己定义。
String [] mediaculm={
MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.DURATION};
//获取reslover对象
ContentResolver mediaresolver=this.getContentResolver();
Cursor cur=mediaresolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mediaculm, null,null,null);
data=new String[cur.getCount()][3];
int i=0;
try{
while(cur.moveToNext()){
data[i][0]=cur.getString(0);
data[i][1]=cur.getString(1);
data[i][2]=cur.getString(2);
i++;
Log.i(tag,"歌曲名称"+cur.getString(0)+"歌曲长度"+cur.getString(2));
}
}catch(Exception e){
Log.i(tag,e.getMessage());
}
四,具体功能的实现,首先需要在播放时为其添加播放结束时的时间来实现播放模式,这种情况都需要实现调用stop,否则可能会让程序down掉
mPlay.setOnCompletionListener(new OnCompletionListener(){
@Override
public void onCompletion(MediaPlayer mPlay) {
// TODO Auto-generated method stub
playbutton playmode=(playbutton)MainActivity.this.findViewById(R.id.btnMode);
String Name=null;
switch(playmode.getPlayMode()){
//单曲循环
case 1:
mPlay.stop();
Log.i("player","目前曲目"+mId);
Name=data[mId][0];
playmedia(Name);
break;
//顺序播放
case 2:
if(mId1){
mId++;
mPlay.stop();
Log.i("player","目前曲目"+mId);
Name=data[mId][0];
playmedia(Name);
}else{
mPlay.stop();
playmode.setflag(true);
}
break;
//随机播放
case 3:
mId=(int) (Math.random()*data.length);
mPlay.stop();
Log.i("player","目前曲目"+mId);
Name=data[mId][0];
playmedia(Name);
break;
//全曲循环
case 4:
playNext();
break;
default:
}
}
});
这样一个基本的音乐播放软件就完成了,下面是源代码:
package com.example.mp3site;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private ProgressBar mPbar=null;
private static final String tag="player";
private MediaPlayer mPlay=null;
private Boolean flag_stop=false;
private String firstMusicName=null;
private String data[][]=null;
private int mId=-1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPbar=(ProgressBar)this.findViewById(R.id.progressBar1);
ListView lview=(ListView)this.findViewById(R.id.listView1);
//首先获取media中的数据
String [] mediaculm={
MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.DURATION};
//获取reslover对象
ContentResolver mediaresolver=this.getContentResolver();
Cursor cur=mediaresolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mediaculm, null,null,null);
data=new String[cur.getCount()][3];
int i=0;
try{
while(cur.moveToNext()){
data[i][0]=cur.getString(0);
data[i][1]=cur.getString(1);
data[i][2]=cur.getString(2);
i++;
Log.i(tag,"歌曲名称"+cur.getString(0)+"歌曲长度"+cur.getString(2));
}
}catch(Exception e){
Log.i(tag,e.getMessage());
}
firstMusicName=data[0][0];
TextView tv=(TextView)MainActivity.this.findViewById(R.id.MusicName);
tv.setText(firstMusicName);
setclick();
MyAdapter adapter=new MyAdapter(data);
lview.setAdapter(adapter);
}
public void playmedia(String Name){
TextView tv=(TextView)MainActivity.this.findViewById(R.id.MusicName);
tv.setText(Name);
mPlay=new MediaPlayer();
//将进度条置为0
mPbar.setProgress(0);
//如果在播放,就重置播放器
if(mPlay.isPlaying()){
mPlay.reset();
}
try{
//设置播放文件流
String datapath="/sdcard/"+Name+".mp3";
mPlay.setDataSource(datapath);
//缓冲
mPlay.setOnPreparedListener(new OnPreparedListener(){
@Override
public void onPrepared(MediaPlayer mPlay) {
// TODO Auto-generated method stub
//获取Mp3文件长度
int width=mPlay.getDuration();
mPbar.setMax(width/1000);
//开始播放
mPlay.start();
//单进程可能会阻塞
new Thread(new refresh(width,mPlay)).start();
}
});
mPlay.prepareAsync();
mPlay.setOnCompletionListener(new OnCompletionListener(){
@Override
public void onCompletion(MediaPlayer mPlay) {
// TODO Auto-generated method stub
playbutton playmode=(playbutton)MainActivity.this.findViewById(R.id.btnMode);
String Name=null;
switch(playmode.getPlayMode()){
//单曲循环
case 1:
mPlay.stop();
Log.i("player","目前曲目"+mId);
Name=data[mId][0];
playmedia(Name);
break;
//顺序播放
case 2:
if(mId1){
mId++;
mPlay.stop();
Log.i("player","目前曲目"+mId);
Name=data[mId][0];
playmedia(Name);
}else{
mPlay.stop();
playmode.setflag(true);
}
break;
//随机播放
case 3:
mId=(int) (Math.random()*data.length);
mPlay.stop();
Log.i("player","目前曲目"+mId);
Name=data[mId][0];
playmedia(Name);
break;
//全曲循环
case 4:
playNext();
break;
default:
}
}
});
//mPlay.getCurrentPosition();
}catch(Exception e){
Log.i(tag,e.getMessage());
}
}
public class refresh implements Runnable{
private int width;
private MediaPlayer mPlay=null;
public refresh(int width, MediaPlayer mPlay){
this.width=width;
this.mPlay=mPlay;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(mPlay.isPlaying()){
//获取当前播放位置
int position=mPlay.getCurrentPosition()/1000;
//计算进度条位置
mPbar.setProgress(position);
}
}
}
private void setclick(){
playbutton play=(playbutton) this.findViewById(R.id.btnStart);
play.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v1) {
// TODO Auto-generated method stub
playbutton play=(playbutton) MainActivity.this.findViewById(R.id.btnStart);
Log.i("player","now status"+play.getflag());
if(play.getflag()==false){
if(mPlay!=null&&mPlay.isPlaying()){
flag_stop=true;
mPlay.pause();
}
}else{
if(flag_stop){
mPlay.start();
flag_stop=false;
}else{
if(firstMusicName!=null){
mId=0;
playmedia(firstMusicName) ;
}
//mPlay.getCurrentPosition();
}
}
}
});
playbutton forward=(playbutton) this.findViewById(R.id.btnforward);
forward.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
playNext();
}
});
playbutton backward=(playbutton) this.findViewById(R.id.btnbackward);
backward.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
playBack();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public class MyAdapter extends BaseAdapter{
private String list[][]=null;
public MyAdapter(String list[][]){
this.list=list;
Log.i(tag,list[0][0]+list.length);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.length;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View vi, ViewGroup vg) {
// TODO Auto-generated method stub
TextView tview=new TextView(MainActivity.this);
int timeLine=Integer.parseInt(list[position][2]);
int timeLinem=timeLine/1000/60;
int timeLines=(timeLine-timeLinem*60*1000)/1000;
String time=timeLinem+"'"+timeLines;
tview.setText(list[position][0]+" "+time);
tview.setTag(list[position][0]);
tview.setId(position);
tview.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View vi) {
// TODO Auto-generated method stub
String Name=vi.getTag().toString();
mId=vi.getId();
Log.i("player","目前曲目"+mId);
TextView tv=(TextView)MainActivity.this.findViewById(R.id.MusicName);
tv.setText(Name);
if(mPlay!=null){
if(mPlay.isPlaying()){
mPlay.stop();
}
}
playmedia(Name);
playbutton play=(playbutton) MainActivity.this.findViewById(R.id.btnStart);
play.setflag(true);
Log.i(tag, "所获得歌曲名称是"+Name);
//ListView v=(ListView)MainActivity.this.findViewById(R.id.view);
//v.setVisibility(View.GONE);
}
});
//vg.addView(tview);
return tview;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void playNext(){
if(data!=null){
if(mId==data.length-1){
mId=0;
}else{
mId++;
}
mPlay.stop();
Log.i("player","目前曲目"+mId);
String Name=data[mId][0];
playmedia(Name);
}
}
public void playBack(){
if(data!=null){
if(mId==0){
mId=data.length-1;
}else{
mId--;
}
mPlay.stop();
Log.i("player","目前曲目"+mId);
String Name=data[mId][0];
playmedia(Name);
}
}
public class myspinnerAdapter extends BaseAdapter{
private Context context;
private String [][]data;
public myspinnerAdapter( Context context,String [][]data){
this.context=context;
this.data=data;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return data.length;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View v1, ViewGroup vg) {
// TODO Auto-generated method stub
LayoutInflater inflater=LayoutInflater.from(context);
v1=inflater.inflate(R.layout.musicitem,null);
ListView lview=(ListView)v1.findViewById(R.id.listView1);
MyAdapter adapter=new MyAdapter(data);
lview.setAdapter(adapter);
return v1;
}
}
}
playbutton.java
package com.example.mp3site;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class playbutton extends View {
private ImageView image = new ImageView(this.getContext());
private Button button = new Button(this.getContext());
private Paint pressedpaint,unpresspaint,boxpaint,paint;
private int width,height;
private static final int defaultType = -1;
private static final int start = 0;
private static final int forward = 1;
private static final int backward = 2;
private static final int query = 3;
private static final int mode = 4;
private Boolean flag_startstop=false;//false为start,true为stop
private int type;
private int playmode=4;
private Boolean pressed=false;
private static final int MODE_LOOP_ONE=1;//单曲循环,顺序播放,随机播放,全部重复播放
private static final int MODE_SEQ=2;
private static final int MODE_RANDOM=3;
private static final int MODE_ALL=4;
public playbutton(Context context, AttributeSet attrs) {
super(context, attrs);
//获取所定义的值
TypedArray typeArray=context.obtainStyledAttributes(attrs,R.styleable.CustomAudioPlayButton);
type=typeArray.getInt(R.styleable.CustomAudioPlayButton_ButtonValue, defaultType);
Log.i("player","start draw"+type);
typeArray.recycle();
InitView();
//设置为可点击
this.setClickable(true);
//设置触摸事件
this.setOnTouchListener(new onTouch());
// TODO Auto-generated constructor stub
}
public int getPlayMode(){
return playmode;
}
public Boolean getflag(){
return flag_startstop;
}
public void InitView(){
//定义按压时的画笔
pressedpaint=new Paint();
pressedpaint.setColor(Color.GREEN);
pressedpaint.setStyle(Paint.Style.STROKE);
pressedpaint.setAntiAlias(true);
pressedpaint.setStrokeWidth(1);
//定义未按压时画笔
unpresspaint=new Paint();
unpresspaint.setColor(Color.BLACK);
unpresspaint.setStyle(Paint.Style.STROKE);
unpresspaint.setAntiAlias(true);
unpresspaint.setStrokeWidth(1);
//容器画笔
boxpaint=new Paint();
boxpaint.setColor(Color.BLACK);
boxpaint.setStyle(Paint.Style.STROKE);
boxpaint.setAntiAlias(true);
boxpaint.setStrokeWidth(1);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
//获取长宽高
width=this.getMeasuredWidth();
height=this.getMeasuredHeight();
Log.i("player","start draw"+width+" "+height);
//按压时改变按钮颜色
if(pressed){
canvas.drawColor(Color.BLUE);
}
//根据type来画图
switch(type){
case start:
drawstartButton(canvas,pressed);
break;
case forward:
drawforward(canvas,pressed);
break;
case backward:
drawbackward(canvas,pressed);
break;
case query:
drawquery(canvas,pressed);
break;
case mode:
drawMode(canvas,pressed);
break;
default:
}
final Rect boxrect=canvas.getClipBounds();
canvas.drawRect(boxrect, boxpaint);
super.onDraw(canvas);
}
public void drawquery(Canvas canvas,Boolean pressed){
float []point={(float) (0.1*width),(float)0.3*height,(float)0.5*width,(float) (0.9*width),(float) (0.5*height)};
canvas.drawLine(point[0], point[1],point[3],point[1], unpresspaint);
canvas.drawLine(point[0], point[1],point[2],point[4], unpresspaint);
canvas.drawLine(point[3], point[1],point[2],point[4], unpresspaint);
}
public void setflag(Boolean flag){
flag_startstop=flag;
invalidate();
}
public void drawstartButton(Canvas canvas,Boolean pressed){
//如果是true,画双竖线,false话右三角
float []point={(float) (0.3*width),(float)0.1*height,(float)0.9*height,(float) (0.6*width),(float) (0.5*height)};
Log.i("player","start draw start");
if(flag_startstop){
canvas.drawLine(point[0], point[1],point[0],point[2], pressedpaint);
canvas.drawLine(point[3], point[1],point[3],point[2], pressedpaint);
}else{
canvas.drawLine(point[0], point[1],point[0],point[2], unpresspaint);
canvas.drawLine(point[0], point[1],point[3],point[4], unpresspaint);
canvas.drawLine(point[0], point[2],point[3],point[4], unpresspaint);
}
}
public void drawforward(Canvas canvas,Boolean pressed){
float []point={(float) (0.3*width),(float)0.1*height,(float)0.9*height,(float) (0.5*width),(float) (0.5*height),(float) (0.7*width)};
canvas.drawLine(point[0], point[1],point[0],point[2], unpresspaint);
canvas.drawLine(point[0], point[1],point[3],point[4], unpresspaint);
canvas.drawLine(point[0], point[2],point[3],point[4], unpresspaint);
canvas.drawLine(point[3], point[1],point[3],point[2], unpresspaint);
canvas.drawLine(point[3], point[1],point[5],point[4], unpresspaint);
canvas.drawLine(point[3], point[2],point[5],point[4], unpresspaint);
}
public void drawbackward(Canvas canvas,Boolean pressed){
float []point={(float) (0.3*width),(float)0.1*height,(float)0.9*height,(float) (0.5*width),(float) (0.5*height),(float) (0.7*width)};
canvas.drawLine(point[3], point[1],point[3],point[2], unpresspaint);
canvas.drawLine(point[3], point[1],point[0],point[4], unpresspaint);
canvas.drawLine(point[3], point[2],point[0],point[4], unpresspaint);
canvas.drawLine(point[5], point[1],point[5],point[2], unpresspaint);
canvas.drawLine(point[5], point[1],point[3],point[4], unpresspaint);
canvas.drawLine(point[5], point[2],point[3],point[4], unpresspaint);
}
public void drawMode(Canvas canvas,Boolean pressed){
//播放方式
canvas.save();
Log.i("player","当前模式"+playmode);
switch(playmode){
case MODE_LOOP_ONE:
canvas.drawCircle(width/2, height/2, 3, unpresspaint);
break;
case MODE_SEQ:
canvas.drawCircle(width/2-3, height/2, 3, unpresspaint);
canvas.drawCircle(width/2+3, height/2, 3, unpresspaint);
break;
case MODE_RANDOM:
canvas.drawCircle(width/2-6, height/2, 3, unpresspaint);
canvas.drawCircle(width/2+6, height/2, 3, unpresspaint);
canvas.drawCircle(width/2, height/2, 3, unpresspaint);
break;
case MODE_ALL:
canvas.drawCircle(width/2-3, height/2, 3, unpresspaint);
canvas.drawCircle(width/2+3, height/2, 3, unpresspaint);
canvas.drawCircle(width/2-9, height/2, 3, unpresspaint);
canvas.drawCircle(width/2+9, height/2, 3, unpresspaint);
break;
default:
}
canvas.restore();
}
public class onTouch implements OnTouchListener{
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressed = true;
if(type==start){
if(flag_startstop){
flag_startstop=false;
}else{
flag_startstop=true;
}}else if(type==mode){
if(playmode<4){
playmode++;
}else{
playmode=1;
}
}
invalidate();
break;
case MotionEvent.ACTION_UP:
pressed = false;
invalidate();
break;
}
return false;
}
}
}
下期预告:
少年、クントをやらないか