Android使用多点触摸之绘图板

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</RelativeLayout>

MainAvtivity.java

public class MainActivity extends Activity implements OnTouchListener{

	ImageView imageView;
	Bitmap bitmap;
	Canvas canvas;
	Paint paint;

	float startx = 0f;
	float starty = 0f;
	float endx = 0f;
	float endy = 0f;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//全屏显示
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		//横屏显示
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		setContentView(R.layout.activity_main);

		imageView = (ImageView)findViewById(R.id.imageView);
		Display display = getWindowManager().getDefaultDisplay();

		bitmap = Bitmap.createBitmap(display.getWidth(), display.getHeight(), Bitmap.Config.ARGB_8888);
		canvas = new Canvas(bitmap);
		//设置画笔
		paint = new Paint();
		paint.setColor(Color.RED);
		paint.setStrokeWidth(5);
		imageView.setImageBitmap(bitmap);
		imageView.setOnTouchListener(this);
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {

		switch(event.getAction()){

		case MotionEvent.ACTION_DOWN:
			startx = event.getX();
			starty = event.getY();
			break;

		case MotionEvent.ACTION_UP:
			break;

		case MotionEvent.ACTION_MOVE:
			endx = event.getX();
			endy = event.getY();

			canvas.drawLine(startx, starty, endx, endy, paint);
			//重绘图像
			imageView.invalidate();
			//重新设置起始坐标
			startx = endx;
			starty = endy;
			break;

		case MotionEvent.ACTION_CANCEL:
			break;

	    default:
			break;
		}

		return true;
	}
}

git clone https://github.com/lnmcc/TouchDrawExample.git

Android图像编辑和处理之调整图像明亮度

ColorMatrix是一个4 x 5的矩阵,M' = M * CM
M' = (R', G', B', A') : 为结果颜色矩阵;
M = (R, G, B, A) : 为源颜色矩阵;
CM = (a11 a12 a13 a14 a15)
(a21 a22 a23 a24 a25)
(a31 a32 a33 a34 a35)
(a41 a42 a43 a44 a45) : 为转换矩阵(ColorMatrix);
ColorMatrix的前4列作用于R, G, B, A, 第五列作用与亮度值。
通过ColorMatrix 不仅可以调整亮度还可以调整各个颜色值。

使用ColorMatrix的基本步骤 :

  1. 定义一个ColorMatrix : ColorMatrix cm = new ColorMatrix()
  2. 设置我们需要的颜色转化矩阵 : cm.set(new float[]{})
  3. 给当前Paint设置ColorFilter : paint.setColorFilter(new ColorMatrixColorFilter(cm))
  4. 使用设置好的Paint绘图

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/ChoosePictureButton"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Choose Picture" />

        <SeekBar
            android:id="@+id/brightnessSB"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/ChoosePictureButton"
            android:max="200"
            android:progress="100" />
    </RelativeLayout>

    <ImageView
        android:id="@+id/ChooseImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/AlteredImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

alt none

MainActivity.java:

public class MainActivity extends Activity implements OnClickListener {

	private static final int REQUEST_CODE = 0;

	ImageView chosenImageView;
	ImageView alteredImageView;

	Bitmap alteredBitmap;
	Bitmap bmp;

	Button choosePicture;
	//亮度
	float brightness;

	//调节亮度的进度条
	SeekBar brightnessSB;

	Canvas canvas;
	Paint paint;
	ColorMatrix cm;
	Matrix matrix;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

		alteredImageView = (ImageView) findViewById(R.id.AlteredImageView);
		chosenImageView = (ImageView) findViewById(R.id.ChooseImageView);
		choosePicture = (Button) findViewById(R.id.ChoosePictureButton);
		choosePicture.setOnClickListener(this);
		//变换矩阵,可以通过这个矩阵做很多图像缩放、平移和旋转等操作
		matrix = new Matrix();
		//颜色矩阵
		cm = new ColorMatrix();
		//画笔
		paint = new Paint();
		paint.setColorFilter(new ColorMatrixColorFilter(cm));

		brightnessSB = (SeekBar) findViewById(R.id.brightnessSB);
		brightnessSB.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

					@Override
					public void onStopTrackingTouch(SeekBar seekBar) {
					}

					@Override
					public void onStartTrackingTouch(SeekBar seekBar) {
					}

					@Override
					public void onProgressChanged(SeekBar seekBar,
							int progress, boolean fromUser) {

						if (null != bmp) {
							//seekbar的范围为[0, 200]
							//brightness的范围为[-100, 100]
							brightness = (float) (progress - 100);
							cm.set(new float[] { 1, 0, 0, 0, brightness,
									             0, 1, 0, 0, brightness, 
									             0, 0,1, 0, brightness, 
									             0, 0, 0, 1, 0 });

							paint.setColorFilter(new ColorMatrixColorFilter(cm));
							canvas.drawBitmap(bmp, matrix, paint);
							alteredImageView.setImageBitmap(alteredBitmap);
						} else {
							brightnessSB.setProgress(100);
						}
					}
				});
	}

	@Override
	public void onClick(View v) {
		//请求Android的gallery程序,选取sdcard中的照片
		Intent choosePictureIntent = new Intent(Intent.ACTION_PICK,
				MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
		startActivityForResult(choosePictureIntent, REQUEST_CODE);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

		if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {

			brightnessSB.setProgress(100);
			Uri imageFileUri = data.getData();
			Display currentDisplay = getWindowManager().getDefaultDisplay();
			int dw = currentDisplay.getWidth();
			int dh = currentDisplay.getHeight() / 3 - 100;
			try {
				BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
				bmpFactoryOptions.inJustDecodeBounds = true;
				bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);

				int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight
						/ (int) dh);
				int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth
						/ (int) dw);

				if (heightRatio > 1 && widthRatio > 1) {
					if (heightRatio > widthRatio) {
						bmpFactoryOptions.inSampleSize = heightRatio;
					} else {
						bmpFactoryOptions.inSampleSize = widthRatio;
					}
				}
				bmpFactoryOptions.inJustDecodeBounds = false;
				bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);

				alteredBitmap = Bitmap.createBitmap(bmp.getWidth(),
						bmp.getHeight(), bmp.getConfig());

				canvas = new Canvas(alteredBitmap);
				canvas.drawBitmap(bmp, matrix, paint);

				alteredImageView.setImageBitmap(alteredBitmap);
				chosenImageView.setImageBitmap(bmp);

			} catch (Exception e) {
				Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
			}
		}
	}
}

alt none alt none alt none

git clone https://github.com/lnmcc/brightnessExample.git

ref: 《Android多媒体开发高级编程》

repo 1.12.4 gpg key 错误

自从repo升级到 1.12.4后,repo init的时候就一直报错:

repo 1.12.4

gpg: Signature made Wed Oct  2 00:44:27 2013 CST using RSA key ID 692B382C
gpg: Can't check signature: public key not found
error: could not verify the tag 'v1.12.4'

查了半天原来是android doc没有更新,解决方法是不要使用

curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo

下载的repo,使用下面的网址即可:

curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo

Android Activity布局(layout)介绍

LinearLayout

LinearLayout——线性布局,顾名思义,LinearLayout中的组件按照线性排列,每一个组件都位于前一个组件的后面。LinearLayout又可以分为水平线性布局和垂直线性布局。

垂直线性布局

在垂直线性布局中,组件从上至下依次排列。可以使用android:layout_weight属性来控制组件占据空间的比例:当layout_width或layout_height设置为0dp的时候,数值越高占据的比例越大;当layout_width或layout_height设置为match_parent时,数值越高占据的比例越小。

alt none

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:text="TextView 1" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="TextView 2" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="TextView 3" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView 4" />

水平线性布局

水平线性布局中的组件按照水平方向平铺排列,组件所占空间的比例跟layout_weight的值成正比。

alt none

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="TV 1" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:text="TV 2" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TV 3" />

&lt/LinearLayout>

RelativeLayout

RelativeLayout——相对布局。在相对布局中,组件的位置关系是相对的,我们可以用下面的几个属性来控制组件之间的相对关系:

属性名 相对位置
layout_toLeftOf 位于指定组件的左侧
layout_toRightOf 位于指定组件的右侧
layout_above 位于指定组件的上方
layout_below 位于指定组件的下方
layout_alignParentLeft 与父组件左对齐
layout_alignParentRight 与父组件右对齐
layout_alignParentTop 与父组件上对齐
layout_alignParentBottom 与父组件下对齐
layout_centerInParent 与父组件居中对齐
layout_centerHorizontal 垂直居中
layout_centerVertica 水平居中

alt none

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <Button
        android:id="@+id/tv1"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#ff00ffff"
        android:gravity="center"
        android:text="1" />

    <Button
        android:id="@+id/tv2"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_below="@+id/tv1"
        android:background="#ffffff00"
        android:gravity="center"
        android:text="2" />

    <Button
        android:id="@+id/tv3"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_below="@+id/tv2"
        android:background="#ffff00ff"
        android:gravity="center"
        android:text="3" />

    <Button
        android:id="@+id/tv4"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_below="@+id/tv2"
        android:layout_toRightOf="@+id/tv3"
        android:background="#ffff0000"
        android:gravity="center"
        android:text="4" />

</RelativeLayout>

FrameLayout

FrameLayout——帧布局。帧布局中的所有组件都放在布局的左上角,所有组件会层叠在一起。下面的例子展示的是一个相机应用,这个应用的布局中有一个按钮和一个文本框飘浮在一个SurfaceView之上,这样我们既可以做到全屏预览相机画面,又可以把拍照按钮和一些信息显示在预览画面之上。

alt none

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/FL"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >

        <SurfaceView
            android:id="@+id/CV"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

        <LinearLayout
            android:id="@+id/LL"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/TV"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="10"
                android:textSize="100dip" />

            <Button
                android:id="@+id/BT"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Take Picture" />
        </LinearLayout>
    </FrameLayout>

</LinearLayout>

Android Use Camera 之捕捉图像并修改图像属性

git clone https://github.com/lnmcc/CameraExample.git

下面的例子将展示如何通过Camera Intent捕捉图像并修改图像属性的方法。

activity_main.xml

alt none

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" 
    tools:context=".MainActivity" >

	<ImageView
	    android:id="@+id/imgView"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content" />

	<TextView
	    android:id="@+id/TitleTV"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:text="Title" />

	<EditText
            android:id="@+id/TitleET"
	    android:layout_width="fill_parent"
	    android:layout_height="wrap_content" />

	<TextView 
            android:id="@+id/DescriptionTV"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:text="Description" />

	<EditText
            android:id="@+id/DescriptionET" 
	    android:layout_width="fill_parent"
	    android:layout_height="wrap_content" />

	<Button
	    android:id="@+id/TakePictureBT"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:text="Take Picture" />

	<Button
	    android:id="@+id/SaveDataBT"
	    android:layout_width="wrap_content"
	    android:layout_height="wrap_content"
	    android:text="Save Data" />

</LinearLayout>

MainActivity.java

public class MainActivity extends Activity  {

        //标记一个Request,后面onActivityResult将检查这个标记来确认是由本activity发出的request
        static final int CAMERA_REQUEST = 1; 
        static final String TAG = "MainActivity";

	Uri imageFileUri;
	ImageView imgView;
	Button takePictureBT;
	Button saveDataBT;
	TextView titleTV;
	TextView descriptionTV;
	EditText titleET;
	EditText descriptionET;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		imgView = (ImageView)findViewById(R.id.imgView);
		takePictureBT = (Button)findViewById(R.id.TakePictureBT);
		saveDataBT = (Button)findViewById(R.id.SaveDataBT);
		titleTV = (TextView)findViewById(R.id.TitleTV);
		descriptionTV  = (TextView)findViewById(R.id.DescriptionTV);
		titleET = (EditText)findViewById(R.id.TitleET);
		descriptionET = (EditText)findViewById(R.id.DescriptionET);

		//View.GONE跟*View.INVISIBLE的区别在于前者隐藏控件并且不占用布局空间
		imgView.setVisibility(View.GONE);
		saveDataBT.setVisibility(View.GONE);
		titleTV.setVisibility(View.GONE);
		descriptionTV.setVisibility(View.GONE);
		titleET.setVisibility(View.GONE);
		descriptionET.setVisibility(View.GONE);

		takePictureBT.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View arg0) {
                                //获取内容提供器,并插入一个空图像,这里还传入了一个空的图像属性ContentValues以方 
                                //便后面我们手动更改图像属性。insert()将返回图像的uri。                       
				imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues());
                                //通过Intent调用系统Camera应用。
				Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
				i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);
				startActivityForResult(i, CAMERA_REQUEST);
			}
		});

		//当点击saveData按钮后,读取TextView中的信息并更新到图像属性中。
		saveDataBT.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				ContentValues contentValues = new ContentValues(3);
                                //修改图像名称
				contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, titleET.getText().toString());
                                //修改图像描述
				contentValues.put(MediaStore.Images.Media.DESCRIPTION, descriptionET.getText().toString());
                                //通过内容提供器更新图像属性,返回被更新的行数。
				int upRows = getContentResolver().update(imageFileUri, contentValues, null, null);
				Toast.makeText(MainActivity.this, "Record Updated" + Integer.valueOf(upRows).toString(), Toast.LENGTH_SHORT).show();

				takePictureBT.setVisibility(View.VISIBLE);
				imgView.setVisibility(View.GONE);
				saveDataBT.setVisibility(View.GONE);
				titleTV.setVisibility(View.GONE);
				descriptionTV.setVisibility(View.GONE);
				titleET.setVisibility(View.GONE);
				descriptionET.setVisibility(View.GONE);
			}
		});
	}

        //startActivityForResult()调用Camera Intent,当Camera Intent完成任务后返回到MainActivity的时候会调用这个方法 
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);

                //首先检查一下这个Camera Intent是否是由MainActivity请求的 
                //然后检查一下Camera Intent 是否执行成功                 
		if(requestCode == CAMERA_REQUEST && resultCode == RESULT_OK) {
			takePictureBT.setVisibility(View.GONE);
			saveDataBT.setVisibility(View.VISIBLE);
			imgView.setVisibility(View.VISIBLE);
			titleTV.setVisibility(View.VISIBLE);
			descriptionTV.setVisibility(View.VISIBLE);
			descriptionET.setVisibility(View.VISIBLE);
			titleET.setVisibility(View.VISIBLE);

                        //图像的显示区域大小
			int dw = 200;
			int dh = 200;

			try {
				BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();

                                //inJustDecodeBound=true指示BitmapFactory只计算图像的大小,不要做真正的解码
				bmpFactoryOptions.inJustDecodeBounds = true;

				Bitmap bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri),
						null, bmpFactoryOptions);

                                //计算实际图像和显示区域的缩放比率
				int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight / (float)dh);
				int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth / (float)dw);

				if(heightRatio > 1 && widthRatio > 1) {
					if(heightRatio > widthRatio) {

                                                //inSampleSize指示BitmapFactory对图像进行缩放
                                                //比如inSampleSize=2,则图片将被缩小到1/2
						bmpFactoryOptions.inSampleSize = heightRatio;
					} else {
						bmpFactoryOptions.inSampleSize = widthRatio;
					}
				}

                                //inJustDecodeBound=fase指示BitmapFactory开始真正的图像解码
				bmpFactoryOptions.inJustDecodeBounds = false;
				bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri),
						null, bmpFactoryOptions);

                                //在ImageView控件中显示图像
				imgView.setImageBitmap(bmp);
			} catch(FileNotFoundException e) {
				Log.v(TAG, e.toString());
			}
		}
	}
}

查看系统image数据库确认图像属性被更新

$ sqlite3 /data/data/com.android.providers.media/databases/external-ae126518.db
$ sqlite> . mode columns
$ sqlite> . header on
$ sqlite> select * from images;

返回数据:

139  /mnt/sdcard/DCIM/Camera/1380476346865.jpg  0  keyboard  image/jpeg  1380476346865                1380476346  1380476346     apple keyboard    1506676782  Camera

这里我把Title设置成keyboard,Description设置成apple keyboard

完成后的效果

alt none alt none alt none