SurfaceView与其他控件
一、几点说明
(1) SurfaceView是view的子类,没有实现ViewGroup类,不能在SurfaceView上实现添加其他控件。
(2) 若想SurfaceView与其他控件实现在一个Activity中出现,必须把SurfaceView对象和其他对象放到一个布局文件中。
(3) 要在Layout.xml中使用SurfaceView控件,SurfaceView必须使用两个参数的构造函数,第二个用来传递该View对象的属性。
(4) android:layout_gravity和 android:gravity的区别,从名字上可以看到,android:gravity是对元素本身说的,元素本身的文本显示在什么地方靠着换个属性设置,不过不设置默认是在左侧的。android:layout_gravity是相对与它的父元素说的,说明元素显示在父元素的什么位置。比如说button: android:layout_gravity 表示按钮在界面上的位置。 android:gravity表示button上的字在button上的位置。
(5) 在SurfaceView中只有创建了对象才可以使用getMeasuredWidth()或者getMeasureHeight()
二、实现步骤
①声明MySurfaceView类继承SurfaceView实现Callback、runnable接口。
②在Activity类中获得控件的引用给控件设置监听器。
③在MySurfaceView中实现draw()方法,而不是覆写SurfaceView的draw()方法,由于SurfaceView的draw()方法需要传递一个Canvas对象,不利于线程的调用。
原文:
各位童鞋请你们注意:surfaceview中确实有 onDraw这个方法,但是surfaceview不会自己去调用!!!
而我代码中的ondraw也好 draw也好,都是我自己定义的一个方法。。。放在线程中不断调用的,一定要注意!!
昨天圣诞节,没有出去,而是一天时间全部纠结在如何在SurfaceView中添加组件,例如添加常用的Button,TextView等等、一开始也想着从网上找些资料看看有没有可参考的,但是发现搜到的结果仍是些童鞋对此很疑惑并且也在找寻答案,那么,这里就把圣诞节一天的成果来和各位童鞋分享;
1.因为我们的SurfaceView是个View对于添加的组件其实也是View,如果我们只是一味的想在SurfaceView中添加View组件其实是错误的思想,当然我一开始也是想着直接在SurfaceView中定义或者去使用组件,但是结果肯定是不成功的,因为View不能添加View!
2.既然第一条肯定是错误的,那么我们就应该想到把我们的SurfaceView和组件都放在一个Layout里面,毕竟我们的的SurfaceView也是一个view和其他组件一同放在我们的layout里,那么这样一来肯定就能完成在SurfaceView中添加组件的目的啦。下面先上截图、
大家看到中间白色区域就是我们的SurfaceView啦,最上方是组件TextView,最下方是Button、对的,要的就是这个效果!而不是像前面文章中多个Activity切换,这样都在一个界面中啦。哇哈哈啊。好、下面来看代码吧:
先放上Xml代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:background="#000000" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="horizontal" > <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/textview" android:textSize="20sp" android:textColor="#00FF00" android:gravity="center_horizontal"/> </LinearLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <com.example.surfaceviewlayout.MySurfaceView android:id="@+id/view3d" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"> <Button android:id="@+id/fristButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/firstButton"/> <Button android:id="@+id/secoendButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/secondButton"/> </LinearLayout> </LinearLayout>
以上代码很简单,都是一些布局方式和各个组件一些属性及显示方式的设定,当然主要看如何对我们的SurfaceView如何注册在xml中的,那么每个组件都有id这样为了对后面其交互数据用到,因为我们要对每个组件操作,所以这里都索引了id方面从R文件中取出其对象。
那么,xml我们定义好了,看看代码中如何实现的,这里先说下Activity类中代码:
package com.example.surfaceviewlayout; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.view.WindowManager; import android.widget.Button; import android.widget.TextView; public class SurfaceViewActivity extends Activity { private Button fristButton; private Button secondButton; private TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_surface_view); fristButton = (Button)findViewById(R.id.fristButton); secondButton = (Button)findViewById(R.id.secoendButton); textView = (TextView)findViewById(R.id.textView); fristButton.setOnClickListener(new ButtonListener()); secondButton.setOnClickListener(new ButtonListener()); } class ButtonListener implements OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub if (v == fristButton) { textView.setText("第一个按钮"); MySurfaceView.button_str = "第一个按钮"; } if (v == secondButton) { textView.setText("第二个按钮"); MySurfaceView.button_str = "第二个按钮"; } } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_surface_view, menu); return true; } }
该有的备注在代码后面都备注了,MySurfaceView.button_str,这个是自己的SurfaceView中定义的一个static的变量用来交互数据用到;在那么下面就要看我们的SurfaceView,当在Xml注册需要注意什么了,我半天的时候都花在了这里!!!一定要引起注意,这也是在SurfaceView中并显示组件完成最重要的一步。
先分析:
1.SurfaceView类的创建和实现等等和之前都是一样的,该怎么去写还怎么去写,但是!构造函数一定要注意!
package com.example.surfaceviewlayout; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; public class MySurfaceView extends SurfaceView implements Callback, Runnable { public static String button_str = "显示哪一个按键按下"; //用于和Activity中的Button按钮数据交互 private int center_x; //用于保存SurfaceView的中心的X轴坐标 private int center_y; //用于保存SurfaceView的中心Y的坐标 private int location_x; //用于保存drawText的X轴坐标 private int move_x=5; //每一次移动的值 private Thread thread; //声明线程 private SurfaceHolder surfaceHolder; private Canvas canvas; private Paint paint; private boolean flag; public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub paint = new Paint(); //实现画笔的设置,针对Text,Line,图形等画笔设置不一样 paint.setAntiAlias(true); //设置画笔实现反锯齿 paint.setTextSize(20); //设置字体的大小 paint.setTypeface(Typeface.DEFAULT_BOLD); //设置字体的样式 paint.setColor(Color.BLUE); //设置字体的颜色 surfaceHolder = getHolder(); surfaceHolder.addCallback(this); this.setKeepScreenOn(true); //设置界面是否一直在主屏幕上,没有太大的用处 setFocusable(true); //设置界面设置可以获取焦点 thread = new Thread(this); } @Override public void run() { // TODO Auto-generated method stub while (flag) { draw(); logic(); try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //实现drawText的X轴坐标变换 public void logic() { if (location_x > (center_x +100) || location_x <(center_x -100)) { move_x=-move_x; } location_x += move_x; } public void draw() { try { canvas = surfaceHolder.lockCanvas(); if (canvas != null) { canvas.drawColor(Color.WHITE); canvas.drawText(button_str, location_x, center_y, paint); } } catch (Exception e) { // TODO: handle exception }finally{ if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } } } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { // TODO Auto-generated method stub } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub center_x = getMeasuredWidth()/2; center_y = getMeasuredHeight()/2; location_x = center_x; flag = true; thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub flag = false; } }
这里解释下备注1: 这里有两个构造函数,当然我们用哪个都是可以的,但是在此时我们需要明确我们到底要使用哪个。
一个参数的构造函数:如果是new出来的此类实例肯定是没有问题,但是我们为了能在显示SurfaceView同时显示别的组件,所以把自定义的SurfaceView也当作组件注册在了main——xml中,所以这里需要注意,当在xml中注册的就必须在SurfaceView中使用这种含有两个参数的构造函数的方法, xml初始化的时候会调用两个参数的这个构造方法,(当时这个问题困扰了半天的研究时间,最后在一个群友的帮助下才发现是这里出了问题)那么含有两个构造参数的方法里第二个参数指的自定义的组件的一些属性,就像长宽一样,你可以给组件属性,就是通过这个来传递的!
那么在SurfaceView中并一同显示组件也就到底完结了,回顾下,一共分为3步,1.将我们的SurfaceView作为一个组件view和其他组件一同放置到布局中,当然布局的方式和显示的方式大家自己随自己喜欢定义! 2.在我们的SurfaceView中一定要使用两个构造函数的构造函数,一定!一定!就这里有区别,别的还是该怎么处理就怎么处理,就是构造函数换了 3.交互数据,对其按键的绑定在 activity中完成,别把view绑定在咱们的SurfaceView中啊,否则报错- -、
这里说下为什么要在activity中去绑定按键处理而不是在我们的surfaceview中去绑定:
其实根据xml中定义button时的id我们可以通过R.id索引取到button,不管在activity中还是我们的surfaceview中都可以取到,但是!绑定button这一步如果在 surfaceview中去写就一定报错,原因我解释下;
我们在xml中定义我们的surfaceview和组件button、textview等等的时候他们是同一级别的!!而不是把button包含在 surfaceview
里,所以虽然在surfaceview中可以根据id索引到button但绑定的时候是无法找到button的,只有我们的activitysetContentView(R.layout.main);显示的button,所以只能在显示它的activity中去绑定,这里需要注意下;