Android
 Computer >> コンピューター >  >> プログラミング >> Android

Androidでビットマップ画像から円形領域を切り抜く方法を徹底解説

このチュートリアルでは、Androidアプリ開発においてビットマップ(Bitmap)画像から円形の領域を切り抜き、さらに枠線や影を追加して仕上げる方法を、サンプルコード付きで段階的に解説します。

この記事で学べること

  • BitmapFactory.decodeResource() によるリソース画像の読み込み
  • CanvasPaint を使った円形マスクの描画
  • PorterDuff.Mode.SRC_IN による画像の円形クリッピング
  • 円形画像への枠線・影の追加テクニック

手順1:新規プロジェクトの作成

Android Studioを起動し、メニューから File → New Project を選択します。必要な項目をすべて入力して、新しいプロジェクトを作成しましょう。

手順2:res/layout/activity_main.xml の編集

以下のコードを res/layout/activity_main.xml に記述します。画面中央にImageView、右下に「Circular It」ボタンを配置したシンプルなレイアウトです。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:id="@+id/rl"
    android:padding="16dp"
    android:background="#edf2ea">
   <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>
   <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Circular It"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"/>
</RelativeLayout>

手順3:src/MainActivity.java の編集

続いて、src/MainActivity.java に以下のコードを追加します。このクラスには、円形切り抜きを行う getCircularBitmap()、枠線を追加する addBorderToCircularBitmap()、影を追加する addShadowToCircularBitmap() という3つのカスタムメソッドが実装されています。

package com.medkart.sample;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class MainActivity extends AppCompatActivity {
    private Context mContext;
    private Resources mResources;
    private RelativeLayout mRelativeLayout;
    private Button mBTN;
    private ImageView mImageView;
    private Bitmap mBitmap;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // アプリケーションコンテキストを取得
        mContext = getApplicationContext();
        // リソースを取得
        mResources = getResources();
        // XMLレイアウトからウィジェットの参照を取得
        mRelativeLayout = (RelativeLayout) findViewById(R.id.rl);
        mImageView = (ImageView) findViewById(R.id.iv);
        mBTN = (Button) findViewById(R.id.btn);
        // ビットマップのリソースIDを取得
        final int bitmapResourceID =R.drawable.flower;
        // ImageViewに画像を設定
        mImageView.setImageBitmap(BitmapFactory.decodeResource(mResources, bitmapResourceID));
        // Buttonにクリックリスナーを設定
        mBTN.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // drawableリソースからビットマップを取得
                mBitmap = BitmapFactory.decodeResource(mResources, bitmapResourceID);
                // 円形のビットマップを生成
                mBitmap = getCircularBitmap(mBitmap);
                // 円形ビットマップの周囲に枠線を追加
                mBitmap = addBorderToCircularBitmap(mBitmap, 15, Color.WHITE);
                // 円形ビットマップの周囲に影を追加
                mBitmap = addShadowToCircularBitmap(mBitmap, 4, Color.LTGRAY);
                // ImageViewにビットマップを設定
                mImageView.setImageBitmap(mBitmap);
            }
        });
    }
    protected Bitmap getCircularBitmap(Bitmap srcBitmap) {
        // 枠線を含む円形ビットマップの幅を計算
        int squareBitmapWidth = Math.min(srcBitmap.getWidth(), srcBitmap.getHeight());
        // 新しいBitmapインスタンスを初期化
        Bitmap dstBitmap = Bitmap.createBitmap (
            squareBitmapWidth, // 幅
            squareBitmapWidth, // 高さ
            Bitmap.Config.ARGB_8888 // 設定
        );
        Canvas canvas = new Canvas(dstBitmap);
        // 新しいPaintインスタンスを初期化
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        Rect rect = new Rect(0, 0, squareBitmapWidth, squareBitmapWidth);
        RectF rectF = new RectF(rect);
        canvas.drawOval(rectF, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        // コピー先ビットマップの左座標と上座標を計算
        float left = (squareBitmapWidth-srcBitmap.getWidth())/2;
        float top = (squareBitmapWidth-srcBitmap.getHeight())/2;
        canvas.drawBitmap(srcBitmap, left, top, paint);
        // このビットマップに関連付けられたネイティブオブジェクトを解放
        srcBitmap.recycle();
        // 円形ビットマップを返す
        return dstBitmap;
    }
    // 円形ビットマップの周囲に枠線を追加するカスタムメソッド
    protected Bitmap addBorderToCircularBitmap(Bitmap srcBitmap, int borderWidth, int borderColor) {
        // 枠線を含む円形ビットマップの幅を計算
        int dstBitmapWidth = srcBitmap.getWidth()+borderWidth*2;
        // 枠線付きの円形ビットマップ用に新しいBitmapを初期化
        Bitmap dstBitmap = Bitmap.createBitmap(dstBitmapWidth,dstBitmapWidth, Bitmap.Config.ARGB_8888);
        // 新しいCanvasインスタンスを初期化
        Canvas canvas = new Canvas(dstBitmap);
        // ソースビットマップをキャンバスに描画
        canvas.drawBitmap(srcBitmap, borderWidth, borderWidth, null);
        // 枠線を描画するためのPaintインスタンスを初期化
        Paint paint = new Paint();
        paint.setColor(borderColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(borderWidth);
        paint.setAntiAlias(true);
        canvas.drawCircle(
            canvas.getWidth() / 2, // cx
            canvas.getWidth() / 2, // cy
            canvas.getWidth()/2 - borderWidth / 2, // 半径
            paint // Paint
        );
        // このビットマップに関連付けられたネイティブオブジェクトを解放
        srcBitmap.recycle();
        // 枠線付きの円形ビットマップを返す
        return dstBitmap;
    }
    // 円形ビットマップの周囲に影を追加するカスタムメソッド
    protected Bitmap addShadowToCircularBitmap(Bitmap srcBitmap, int shadowWidth, int shadowColor){
        // 影を含む円形ビットマップの幅を計算
        int dstBitmapWidth = srcBitmap.getWidth()+shadowWidth*2;
        Bitmap dstBitmap = Bitmap.createBitmap(dstBitmapWidth,dstBitmapWidth, Bitmap.Config.ARGB_8888);
        // 新しいCanvasインスタンスを初期化
        Canvas canvas = new Canvas(dstBitmap);
        canvas.drawBitmap(srcBitmap, shadowWidth, shadowWidth, null);
        // 円形ビットマップの影を描画するPaint
        Paint paint = new Paint();
        paint.setColor(shadowColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(shadowWidth);
        paint.setAntiAlias(true);
        // 円形ビットマップの周囲に影を描画
        canvas.drawCircle (
            dstBitmapWidth / 2, // cx
            dstBitmapWidth / 2, // cy
            dstBitmapWidth / 2 - shadowWidth / 2, // 半径
            paint // Paint
        );
        srcBitmap.recycle();
        return dstBitmap;
    }
}

コードのポイント解説

getCircularBitmap():円形への切り抜き

元画像の幅と高さのうち短い方を一辺として正方形のBitmapを生成し、その上で drawOval() により正円を描画します。その後、PorterDuffXfermodeSRC_IN モードを設定して元画像を重ねることで、円形の部分だけが残る仕組みです。処理完了後は recycle() を呼び出して不要になった元Bitmapのメモリを解放しており、メモリリーク対策として重要なポイントです。

addBorderToCircularBitmap():枠線の追加

元の円形Bitmapよりも枠線の幅の2倍だけ大きなBitmapを新たに作成し、Paint.Style.STROKE を使って指定色・指定太さの輪郭線を円として描画します。これにより、白い縁取りのある円形画像が完成します。

addShadowToCircularBitmap():影の追加

枠線と同じ考え方で、外側に薄いグレーのリングを描画することで、円形画像に影のような立体感を加えています。

手順4:Manifests/AndroidManifest.xml の確認

最後に、Manifests/AndroidManifest.xml を以下のように記述します。特別な権限は不要で、標準的な構成です。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.medkart.sample">
   <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
   </application>
</manifest>

アプリの実行

それでは、アプリケーションを実行してみましょう。ここでは、実機のAndroidスマートフォンをPCに接続していることを前提に説明します。Android Studioからアプリを起動するには、プロジェクト内のアクティビティファイルを開き、ツールバーの実行(Run)アイコンをクリックしてください。デバイス選択のダイアログで接続中のモバイルデバイスを選ぶと、端末に以下のような初期画面が表示されます。

Androidでビットマップ画像から円形領域を切り抜く方法を徹底解説

ボタンをタップすると、画像が円形に切り抜かれ、白い枠線と影が付いた状態に変化します。

Androidでビットマップ画像から円形領域を切り抜く方法を徹底解説


  1. Androidアプリでフラグメントからアクティビティのメソッドを呼び出す方法【サンプルコード付き】

    このチュートリアルでは、Androidアプリにおいてフラグメントからアクティビティのメソッドを呼び出す実装方法を解説します。フラグメントは単体では動作せず、必ずアクティビティ上に存在するため、getActivity()で親アクティビティの参照を取得し、適切な型にキャストすることで、アクティビティのpublicメソッドを直接呼び出すことができます。 実装手順 ステップ1:新規プロジェクトの作成 Android Studioで新しいプロジェクトを作成します。メニューから「File」⇒「New Project」を選択し、必要な項目をすべて入力してプロジェクトを作成しましょう。 ステップ2:activ

  2. Androidで円形のImageViewに影と境界線を追加する方法【コード例付き】

    はじめにこの記事では、AndroidアプリのImageViewに表示する画像を円形に切り取り、さらに白い境界線と影(外枠)を追加する方法を、実際のコード例とともに段階的に解説します。ポイントは、RoundedBitmapDrawableFactoryで生成できるRoundedBitmapDrawableを使って画像を円形化し、PaintとCanvasを使って境界線や影を描画するところです。手順1:新規プロジェクトを作成するAndroid Studioを開き、メニューから「File」→「New Project」を選択して、必要な項目を入力し新しいプロジェクトを作成します。今回は空のアクティビティ