Android studio之图形与图像处理

前言:
众所周知,决定一个Android应用是否可以被用户接受最重要的方面就是用户界面,为了让我们的Android给用户提供一个更友好的界面,就需要我们在应用中使用和插入图片了。Android系统提供了丰富的图片功能支持,其中就包括处理静态图片和动画等等。
一、使用简单的图片
(1)使用Drawable对象
为Android应用添加了Drawable资源之后,Android SDK会为这份资源在R清单文件中创建一个索引项:R.drawable.file_name。
接下来即可在XML资源文件中通过@drawable/file_name访问该Drawable对象,也可以在Java代码中通过R.drawable.file_name访问改对象。
(2)Bitmap和BitmapFactory
Bitmap代表一个位图,而BitmapDrawable中封装的图片就是一个Bitmap对象,开发者为了把一个Bitmap对象包装成BitmapDrawable对象,可以调用BitmapDrawable的构造器:

BitmapDrawable drawable = new BitmapDrawable(bitmap);

果需要获取BitmapDrawable所包装的Bitmap对象,则可调用 BitmapDrawable中的getBitmap()方法:

Bitmap bitmap = drawable.getBitmap();

(3)下面我们用以上知识实现一个图片查看器
我们这个应用十分简单,仅仅包括一个按钮和一个ImageView,当我们点击按钮时候,程序会自动去搜索目录中的下一张图片:
   
关键代码如下:

import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;


public class MainActivity extends Activity
{
   String[] images = null;
   AssetManager assets = null;
   int currentImg = 0;
   ImageView image;
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      image = (ImageView) findViewById(R.id.image);
      try
      {
         assets = getAssets();
         // 获取目录下所有文件(assets下)
         images = assets.list("");
      }
      catch (IOException e)
      {
         e.printStackTrace();
      }
      final Button next = (Button) findViewById(R.id.next);
      // 为next按钮绑定事件监听器,该监听器将会查看下一张图片
      next.setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View sources)
         {
            if (currentImg >= images.length)
            {
               currentImg = 0;
            }
            // 找到下一个图片文件
            while (!images[currentImg].endsWith(".png")
                  && !images[currentImg].endsWith(".jpg")
                  && !images[currentImg].endsWith(".gif"))
            {
               currentImg++;
               if (currentImg >= images.length)
               {
                  currentImg = 0;
               }
            }
            InputStream assetFile = null;
            try
            {
               assetFile = assets.open(images[currentImg++]);
            }
            catch (IOException e)
            {
               e.printStackTrace();
            }
            BitmapDrawable bitmapDrawable = (BitmapDrawable) image
               .getDrawable();
            if (bitmapDrawable != null
                  && !bitmapDrawable.getBitmap().isRecycled()) 
            {
               bitmapDrawable.getBitmap().recycle();
            }
            // 改变ImageView显示的图片
            image.setImageBitmap(BitmapFactory
               .decodeStream(assetFile)); 
         }
      });
   }
}

结果Gif
在这里插入图片描述

二、绘图
在为我们的应用界面添砖加瓦的过程中,除了可以使用已有的图片外,Android应用还经常需要在运行过程中动态的生成图片,而这样就需要借助于Android的绘图支持了。
   
(1)绘图基础——Canvas、Paint

Android的绘图与Swing中的绘图思路类似即开发一个自定义类,然后让该类继承JPanel,之后重写其中的paint(Graphics g)方法即可。而我们Android中的绘图应继承View组件,并重写其中的onDraw(Canvas canvas)即可,光说不练假把式,接下来我们用一段代码绘制几个集合图形。

关键代码如下:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

public class MyView extends View
{
   public MyView(Context context, AttributeSet set)
   {
      super(context, set);
   }
   @Override
   // 重写该方法,进行绘图
   protected void onDraw(Canvas canvas)
   {
      super.onDraw(canvas);
      canvas.drawColor(Color.WHITE);
      Paint paint = new Paint();
      paint.setAntiAlias(true);
      paint.setColor(Color.BLUE);
      paint.setStyle(Paint.Style.STROKE);
      paint.setStrokeWidth(4);
      int viewWidth = this.getWidth();
      // 绘制圆形
      canvas.drawCircle(viewWidth / 10 + 10, viewWidth / 10 + 10
         , viewWidth / 10, paint);
      // 绘制正方形
      canvas.drawRect(10 , viewWidth / 5 + 20 , viewWidth / 5 + 10
         , viewWidth * 2 / 5 + 20 , paint);
      // 绘制圆形
      canvas.drawCircle(viewWidth * 3 / 10 + 20, viewWidth / 10 + 10
         , viewWidth / 10, paint);
      // 绘制正方形
      canvas.drawRect(viewWidth / 5 + 20 , viewWidth / 5 + 20
         , viewWidth * 2 / 5 + 20 , viewWidth * 2 / 5 + 20 , paint);
      // 为Paint设置渐变器
      Shader mShader = new LinearGradient(0, 0, 40, 60
         , new int[] {Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW }
         , null , Shader.TileMode.REPEAT);
      paint.setShader(mShader);
      //设置阴影
      paint.setShadowLayer(25 , 20 , 20 , Color.GRAY);
      // 绘制圆形
      canvas.drawCircle(viewWidth / 2 + 30, viewWidth / 10 + 10
         , viewWidth / 10, paint);
      // 绘制正方形
      canvas.drawRect(viewWidth * 2 / 5 + 30 , viewWidth / 5 + 20
         , viewWidth * 3 / 5 + 30 , viewWidth * 2 / 5 + 20 , paint);
      canvas.drawText(getResources().getString(R.string.circle)
         , 60 + viewWidth * 3 / 5, viewWidth / 10 + 10, paint);
      canvas.drawText(getResources().getString(R.string.square)
         , 60 + viewWidth * 3 / 5, viewWidth * 3 / 10 + 20, paint);
   }
}

结果截图如下:

在这里插入图片描述
(2)Path类
Android提供的Path是一个非常有用的类,它可以在View上将几个点连成一条路径,然后可以调用Canvas中的drawPath(path,paint)方法沿着该路径进行绘图,这些所谓的绘图效果说起来大家很难理解,下面我们通过一段代码来让大家更好的理解一下这些效果,这个应用绘制了7条路径:

关键代码如下:

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposePathEffect;
import android.graphics.CornerPathEffect;
import android.graphics.DashPathEffect;
import android.graphics.DiscretePathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.graphics.PathEffect;
import android.graphics.SumPathEffect;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends Activity
{
   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(new MyView(this));
   }
   class MyView extends View
   {
      float phase;
      PathEffect[] effects = new PathEffect[7];
      int[] colors;
      private Paint paint;
      Path path;
      public MyView(Context context)
      {
         super(context);
         paint = new Paint();
         paint.setStyle(Paint.Style.STROKE);
         paint.setStrokeWidth(4);
         // 创建并初始化Path
         path = new Path();
         path.moveTo(0, 0);
         for (int i = 1; i <= 40; i++)
         {
            path.lineTo(i * 20, (float) Math.random() * 60);
         }
         // 初始化7个颜色
         colors = new int[] { Color.BLACK, Color.BLUE, Color.CYAN,
            Color.GREEN, Color.MAGENTA, Color.RED, Color.YELLOW };
      }
      @Override
      protected void onDraw(Canvas canvas)
      {
         canvas.drawColor(Color.WHITE);
         // 不使用路径效果
         effects[0] = null;
         // 使用CornerPathEffect路径效果
         effects[1] = new CornerPathEffect(10);
         // 初始化DiscretePathEffect
         effects[2] = new DiscretePathEffect(3.0f, 5.0f);
         // 初始化DashPathEffect
         effects[3] = new DashPathEffect(new float[] { 20, 10, 5, 10 },
               phase);
         // 初始化PathDashPathEffect
         Path p = new Path();
         p.addRect(0, 0, 8, 8, Path.Direction.CCW);
         effects[4] = new PathDashPathEffect(p, 12, phase,
               PathDashPathEffect.Style.ROTATE);
         // 初始化ComposePathEffect
         effects[5] = new ComposePathEffect(effects[2], effects[4]);
         effects[6] = new SumPathEffect(effects[4], effects[3]);
         // 将画布移动到(8、8)处开始绘制
         canvas.translate(8, 8);
         // 依次使用7种不同路径效果、7种不同的颜色来绘制路径
         for (int i = 0; i < effects.length; i++)
         {
            paint.setPathEffect(effects[i]);
            paint.setColor(colors[i]);
            canvas.drawPath(path, paint);
            canvas.translate(0, 60);
         }
         // 改变phase值,形成动画效果
         phase += 1;
         invalidate();
      }
   }
}

结果Gif

在这里插入图片描述

三、实例训练
让我们根据今天所学做两个小小的实例练习吧。
(1)手绘画板

我们主要要实现一个画板,当我们在触摸屏上移动时,就可以在屏幕上绘制任意的图案。
表面上看起来我们可以在画板上自由地画画包括直、曲线,实际上我们还是利用Canvas的drawLine()方法画直线线。当我们在画板上移动时,两次拖动时间发生点的距离很小,我们用多条极短的直线将他们连起来就好像我们画的是曲线。主要我们还是借助Android提供的Path类。

关键代码如下:

View代码:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class DrawView extends View
{
   // 定义记录前一个拖动事件发生点的坐标
   float preX;
   float preY;
   private Path path;
   public Paint paint = null;
   // 定义一个内存中的图片,该图片将作为缓冲区
   Bitmap cacheBitmap = null;
   // 定义cacheBitmap上的Canvas对象
   Canvas cacheCanvas = null;
   public DrawView(Context context, int width , int height)
   {
      super(context);
      // 创建一个与该View相同大小的缓存区
      cacheBitmap = Bitmap.createBitmap(width, height,
         Bitmap.Config.ARGB_8888);
      cacheCanvas = new Canvas();
      path = new Path();
      // 设置cacheCanvas将会绘制到内存中的cacheBitmap上
      cacheCanvas.setBitmap(cacheBitmap);
      // 设置画笔的颜色
      paint = new Paint(Paint.DITHER_FLAG);
      paint.setColor(Color.RED);
      // 设置画笔风格
      paint.setStyle(Paint.Style.STROKE);
      paint.setStrokeWidth(1);
      // 反锯齿
      paint.setAntiAlias(true);
      paint.setDither(true);
   }

   @Override
   public boolean onTouchEvent(MotionEvent event)
   {
      // 获取拖动事件的发生位置
      float x = event.getX();
      float y = event.getY();
      switch (event.getAction())
      {
         case MotionEvent.ACTION_DOWN:
            // 从前一个点绘制到当前点之后,把当前点定义成下次绘制的前一个点
            path.moveTo(x, y);
            preX = x;
            preY = y;
            break;
         case MotionEvent.ACTION_MOVE:
            // 从前一个点绘制到当前点之后,把当前点定义成下次绘制的前一个点
            path.quadTo(preX, preY, x, y);
            preX = x;
            preY = y;
            break;
         case MotionEvent.ACTION_UP:
            cacheCanvas.drawPath(path, paint); 
            path.reset();
            break;
      }
      invalidate();
      // 返回true表明处理方法已经处理该事件
      return true;
   }
   @Override
   public void onDraw(Canvas canvas)
   {
      Paint bmpPaint = new Paint();
      // 将cacheBitmap绘制到该View组件上
      canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint); 
      // 沿着path绘制
      canvas.drawPath(path, paint);
   }
}

颜色、大小代码:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:title="@string/color">
      <menu>
         <!-- 定义一组单选菜单项 -->
         <group android:checkableBehavior="single">
            <!-- 定义多个菜单项 -->
            <item android:id="@+id/red"
                android:title="@string/color_red"/>
            <item android:id="@+id/green"
                android:title="@string/color_green"/>
            <item android:id="@+id/blue"
                android:title="@string/color_blue"/>
         </group>
      </menu>
   </item>
   <item android:title="@string/width">
      <menu>
         <!-- 定义一组菜单项 -->
         <group>
            <!-- 定义三个菜单项 -->
            <item android:id="@+id/width_1"
                android:title="@string/width_1"/>
            <item android:id="@+id/width_3"
                android:title="@string/width_3"/>
            <item android:id="@+id/width_5"
                android:title="@string/width_5"/>
         </group>
      </menu>
   </item>
   <item android:id="@+id/blur" android:title="@string/blur"/>
   <item android:id="@+id/emboss" android:title="@string/emboss"/>
</menu>

结果Gif

在这里插入图片描述

(2)弹球小游戏

该应用实现了一个简单的弹球游戏,其中小球和球拍分别以圆形和矩形代替,小球开始以随机速度向下运动,遇到边框或球拍时小球反弹。球拍则由用户控制,当用户按下向A、向D键时,球拍将会向左、向右移动。

关键代码如下:

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.Window;
import android.view.WindowManager;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;


public class MainActivity extends Activity
{
   private int tableWidth;
   private int tableHeight;
   private int racketY;
   private final int RACKET_HEIGHT = 30;
   private final int RACKET_WIDTH = 90;
   private final int BALL_SIZE = 16;
   private int ySpeed = 15;
   Random rand = new Random();
   private double xyRate = rand.nextDouble() - 0.5;
   private int xSpeed = (int) (ySpeed * xyRate * 2);
   // ballX和ballY代表小球的坐标
   private int ballX = rand.nextInt(200) + 20;
   private int ballY = rand.nextInt(10) + 20;
   // racketX代表球拍的水平位置
   private int racketX = rand.nextInt(200);
   // 游戏是否结束的旗标
   private boolean isLose = false;
   private GameView contentView;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      requestWindowFeature(Window.FEATURE_NO_TITLE);
      // 全屏显示
      getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
         WindowManager.LayoutParams.FLAG_FULLSCREEN);
      // 创建GameView组件
      final GameView gameView = new GameView(this);
      setContentView(gameView);
      // 获取窗口管理器
      WindowManager windowManager = getWindowManager();
      Display display = windowManager.getDefaultDisplay();
      DisplayMetrics metrics = new DisplayMetrics();
      display.getMetrics(metrics);
      // 获得屏幕宽和高
      tableWidth = metrics.widthPixels;
      tableHeight = metrics.heightPixels;
      racketY = tableHeight - 80;
      final Handler handler = new Handler() {
         public void handleMessage(Message msg) {
            if (msg.what == 0x123) {
               gameView.invalidate();
            }
         }
      };
      gameView.setOnKeyListener(new OnKeyListener() 
      {
         @Override
         public boolean onKey(View source, int keyCode, KeyEvent event) {
            // 获取由哪个键触发的事件
            switch (event.getKeyCode()) {
               // 控制挡板左移
               case KeyEvent.KEYCODE_A:
                  if (racketX > 0) racketX -= 10;
                  break;
               // 控制挡板右移
               case KeyEvent.KEYCODE_D:
                  if (racketX < tableWidth - RACKET_WIDTH) racketX += 10;
                  break;
            }
            // 通知gameView组件重绘
            gameView.invalidate();
            return true;
         }
      });
      final Timer timer = new Timer();
      timer.schedule(new TimerTask() 
      {
         @Override
         public void run() {
            // 如果小球碰到左边边框
            if (ballX <= 0 || ballX >= tableWidth - BALL_SIZE) {
               xSpeed = -xSpeed;
            }
            // 如果小球高度超出了球拍位置,且横向不在球拍范围之内,游戏结束
            if (ballY >= racketY - BALL_SIZE
                  && (ballX < racketX || ballX > racketX
                  + RACKET_WIDTH)) {
               timer.cancel();
               // 设置游戏是否结束的旗标为true
               isLose = true;
            }
            // 如果小球位于球拍之内,且到达球拍位置,小球反弹
            else if (ballY <= 0
                  || (ballY >= racketY - BALL_SIZE
                  && ballX > racketX && ballX <= racketX
                  + RACKET_WIDTH)) {
               ySpeed = -ySpeed;
            }
            // 小球坐标增加
            ballY += ySpeed;
            ballX += xSpeed;
            // 发送消息,通知系统重绘组件
            handler.sendEmptyMessage(0x123);
         }
      }, 0, 100);
   }
   class GameView extends View
   {
      Paint paint = new Paint();
      public GameView(Context context)
      {
         super(context);
         setFocusable(true);
      }
      // 重写View的onDraw方法,实现绘画
      public void onDraw(Canvas canvas)
      {
         paint.setStyle(Paint.Style.FILL);
         // 设置去锯齿
         paint.setAntiAlias(true);
         // 如果游戏已经结束
         if (isLose)
         {
            paint.setColor(Color.RED);
            paint.setTextSize(40);
            canvas.drawText("游戏已结束", tableWidth / 2 - 100, 200, paint);
         }
         // 如果游戏还未结束
         else
         {
            // 设置颜色,并绘制小球
            paint.setColor(Color.rgb(255, 0, 0));
            canvas.drawCircle(ballX, ballY, BALL_SIZE, paint);
            // 设置颜色,并绘制球拍
            paint.setColor(Color.rgb(80, 80, 200));
            canvas.drawRect(racketX, racketY, racketX + RACKET_WIDTH,
                  racketY + RACKET_HEIGHT, paint);
         }
      }
   }
}

结果Gif

在这里插入图片描述

作者:空蝉缝花Q
原文地址:https://blog.csdn.net/weixin_43901698/article/details/90581993

  • 3
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值