android手写签名

实现手写签名效果如下
在这里插入图片描述

view中新增SignatureView

public class SignatureView extends View {
	// View state
	private List<TimedPoint> mPoints;
	private boolean mIsEmpty;
	private float mLastTouchX;
	private float mLastTouchY;
	private float mLastVelocity;
	private float mLastWidth;
	private RectF mDirtyRect;

	// Configurable parameters
	private int mMinWidth;
	private int mMaxWidth;
	private float mVelocityFilterWeight;
	private OnSignedListener mOnSignedListener;

	private Paint mPaint = new Paint();
	private Path mPath = new Path();
	private Bitmap mSignatureBitmap = null;
	private Canvas mSignatureBitmapCanvas = null;

	public SignatureView(Context context, AttributeSet attrs) {
		super(context, attrs);

		TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SignatureView, 0, 0);

		// Configurable parameters
		try {
			mMinWidth = a.getDimensionPixelSize(R.styleable.SignatureView_minWidth, convertDpToPx(3));
			mMaxWidth = a.getDimensionPixelSize(R.styleable.SignatureView_maxWidth, convertDpToPx(20));
			mVelocityFilterWeight = a.getFloat(R.styleable.SignatureView_velocityFilterWeight, 0.9f);
			mPaint.setColor(a.getColor(R.styleable.SignatureView_penColor, Color.BLACK));
		} finally {
			a.recycle();
		}

		// Fixed parameters
		mPaint.setAntiAlias(true);



 		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mPaint.setStrokeJoin(Paint.Join.ROUND);
		//mPaint.setStyle(Paint.Style.FILL);  //画笔风格
//		mPaint.setAntiAlias(true);          //抗锯齿
//		mPaint.setStrokeWidth(10);           //画笔粗细
//		mPaint.setTextSize(100);             //绘制文字大小,单位px
		//mPaint.setStrokeWidth(Paint.S);
		// Dirty rectangle to update only the changed portion of the view
		mDirtyRect = new RectF();

		clear();
	}

	/**
	 * Set the pen color from a given resource. If the resource is not found,
	 * {@link Color#BLACK} is assumed.
	 *
	 * @param colorRes
	 *            the color resource.
	 */
	@SuppressLint("ResourceType")
	public void setPenColorRes(int colorRes) {
		try {
			setPenColor(getResources().getColor(colorRes));
		} catch (Resources.NotFoundException ex) {
			setPenColor(getResources().getColor(Color.BLACK));
		}
	}

	/**
	 * Set the pen color from a given color.
	 *
	 * @param color
	 *            the color.
	 */
	public void setPenColor(int color) {
		mPaint.setColor(color);
	}

	/**
	 * Set the minimum width of the stroke in pixel.
	 *
	 * @param minWidth
	 *            the width in dp.
	 */
	public void setMinWidth(float minWidth) {
		mMinWidth = convertDpToPx(minWidth);
	}

	/**
	 * Set the maximum width of the stroke in pixel.
	 *
	 * @param maxWidth
	 *            the width in dp.
	 */
	public void setMaxWidth(float maxWidth) {
		mMaxWidth = convertDpToPx(maxWidth);
	}

	/**
	 * Set the velocity filter weight.
	 *
	 * @param velocityFilterWeight
	 *            the weight.
	 */
	public void setVelocityFilterWeight(float velocityFilterWeight) {
		mVelocityFilterWeight = velocityFilterWeight;
	}

	public void clear() {
		mPoints = new ArrayList<TimedPoint>();
		mLastVelocity = 0;
		mLastWidth = (mMinWidth + mMaxWidth) / 2;
		mPath.reset();

		if (mSignatureBitmap != null) {
			mSignatureBitmap = null;
			ensureSignatureBitmap();
		}

		setIsEmpty(true);

		invalidate();
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (!isEnabled())
			return false;

		float eventX = event.getX();
		float eventY = event.getY();

		switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				getParent().requestDisallowInterceptTouchEvent(true);
				mPoints.clear();
				mPath.moveTo(eventX, eventY);
				mLastTouchX = eventX;
				mLastTouchY = eventY;
				addPoint(new TimedPoint(eventX, eventY));

			case MotionEvent.ACTION_MOVE:
				resetDirtyRect(eventX, eventY);
				addPoint(new TimedPoint(eventX, eventY));
				break;

			case MotionEvent.ACTION_UP:
				resetDirtyRect(eventX, eventY);
				addPoint(new TimedPoint(eventX, eventY));
				getParent().requestDisallowInterceptTouchEvent(true);
				setIsEmpty(false);
				break;

			default:
				return false;
		}

		// invalidate();
		invalidate((int) (mDirtyRect.left - mMaxWidth), (int) (mDirtyRect.top - mMaxWidth),
				(int) (mDirtyRect.right + mMaxWidth), (int) (mDirtyRect.bottom + mMaxWidth));

		return true;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		if (mSignatureBitmap != null) {
			canvas.drawBitmap(mSignatureBitmap, 0, 0, mPaint);
		}
	}

	public void setOnSignedListener(OnSignedListener listener) {
		mOnSignedListener = listener;
	}

	public boolean isEmpty() {
		return mIsEmpty;
	}

	public Bitmap getSignatureBitmap() {
		Bitmap originalBitmap = getTransparentSignatureBitmap();
		Bitmap whiteBgBitmap = Bitmap.createBitmap(originalBitmap.getWidth(), originalBitmap.getHeight(),
				Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(whiteBgBitmap);
		canvas.drawColor(Color.WHITE);
		canvas.drawBitmap(originalBitmap, 0, 0, null);
		return whiteBgBitmap;
	}

	public void setSignatureBitmap(Bitmap signature) {
		clear();
		ensureSignatureBitmap();

		RectF tempSrc = new RectF();
		RectF tempDst = new RectF();

		int dWidth = signature.getWidth();
		int dHeight = signature.getHeight();
		int vWidth = getWidth();
		int vHeight = getHeight();

		// Generate the required transform.
		tempSrc.set(0, 0, dWidth, dHeight);
		tempDst.set(0, 0, vWidth, vHeight);

		Matrix drawMatrix = new Matrix();
		drawMatrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER);

		Canvas canvas = new Canvas(mSignatureBitmap);
		canvas.drawBitmap(signature, drawMatrix, null);
		setIsEmpty(false);
		invalidate();
	}

	public Bitmap getTransparentSignatureBitmap() {
		ensureSignatureBitmap();
		return mSignatureBitmap;
	}

	public Bitmap getTransparentSignatureBitmap(boolean trimBlankSpace) {

		if (!trimBlankSpace) {
			return getTransparentSignatureBitmap();
		}

		ensureSignatureBitmap();

		int imgHeight = mSignatureBitmap.getHeight();
		int imgWidth = mSignatureBitmap.getWidth();

		int backgroundColor = Color.TRANSPARENT;

		int xMin = Integer.MAX_VALUE, xMax = Integer.MIN_VALUE, yMin = Integer.MAX_VALUE, yMax = Integer.MIN_VALUE;

		boolean foundPixel = false;

		// Find xMin
		for (int x = 0; x < imgWidth; x++) {
			boolean stop = false;
			for (int y = 0; y < imgHeight; y++) {
				if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {
					xMin = x;
					stop = true;
					foundPixel = true;
					break;
				}
			}
			if (stop)
				break;
		}

		// Image is empty...
		if (!foundPixel)
			return null;

		// Find yMin
		for (int y = 0; y < imgHeight; y++) {
			boolean stop = false;
			for (int x = xMin; x < imgWidth; x++) {
				if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {
					yMin = y;
					stop = true;
					break;
				}
			}
			if (stop)
				break;
		}

		// Find xMax
		for (int x = imgWidth - 1; x >= xMin; x--) {
			boolean stop = false;
			for (int y = yMin; y < imgHeight; y++) {
				if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {
					xMax = x;
					stop = true;
					break;
				}
			}
			if (stop)
				break;
		}

		// Find yMax
		for (int y = imgHeight - 1; y >= yMin; y--) {
			boolean stop = false;
			for (int x = xMin; x <= xMax; x++) {
				if (mSignatureBitmap.getPixel(x, y) != backgroundColor) {
					yMax = y;
					stop = true;
					break;
				}
			}
			if (stop)
				break;
		}

		return Bitmap.createBitmap(mSignatureBitmap, xMin, yMin, xMax - xMin, yMax - yMin);
	}

	private void addPoint(TimedPoint newPoint) {
		mPoints.add(newPoint);
		if (mPoints.size() > 3) {
			// To reduce the initial lag make it work with 3 mPoints
			// by copying the first point to the beginning.
//			if (mPoints.size() == 3)
//				mPoints.add(0, mPoints.get(0));

			ControlTimedPoints tmp = calculateCurveControlPoints(mPoints.get(0), mPoints.get(1), mPoints.get(2));
			TimedPoint c2 = tmp.c2;
			tmp = calculateCurveControlPoints(mPoints.get(1), mPoints.get(2), mPoints.get(3));
			TimedPoint c3 = tmp.c1;
			Bezier curve = new Bezier(mPoints.get(1), c2, c3, mPoints.get(2));

			TimedPoint startPoint = curve.startPoint;
			TimedPoint endPoint = curve.endPoint;

			float velocity = endPoint.velocityFrom(startPoint);
			velocity = Float.isNaN(velocity) ? 0.0f : velocity;

			velocity = mVelocityFilterWeight * velocity + (1 - mVelocityFilterWeight) * mLastVelocity;

			// The new width is a function of the velocity. Higher velocities
			// correspond to thinner strokes.
			float newWidth = strokeWidth(velocity);

			// The Bezier's width starts out as last curve's final width, and
			// gradually changes to the stroke width just calculated. The new
			// width calculation is based on the velocity between the Bezier's
			// start and end mPoints.
			addBezier(curve, mLastWidth, newWidth);

			mLastVelocity = velocity;
			mLastWidth = newWidth;

			// Remove the first element from the list,
			// so that we always have no more than 4 mPoints in mPoints array.
			mPoints.remove(0);
		}
	}

	private void addBezier(Bezier curve, float startWidth, float endWidth) {
		ensureSignatureBitmap();
		float originalWidth = mPaint.getStrokeWidth();
		float widthDelta = endWidth - startWidth;
		float drawSteps = (float) Math.floor(curve.length());

		for (int i = 0; i < drawSteps; i++) {
			// Calculate the Bezier (x, y) coordinate for this step.
			float t = ((float) i) / drawSteps;
			float tt = t * t;
			float ttt = tt * t;
			float u = 1 - t;
			float uu = u * u;
			float uuu = uu * u;

			float x = uuu * curve.startPoint.x;
			x += 3 * uu * t * curve.control1.x;
			x += 3 * u * tt * curve.control2.x;
			x += ttt * curve.endPoint.x;

			float y = uuu * curve.startPoint.y;
			y += 3 * uu * t * curve.control1.y;
			y += 3 * u * tt * curve.control2.y;
			y += ttt * curve.endPoint.y;

			// Set the incremental stroke width and draw.
			mPaint.setStrokeWidth(startWidth + ttt * widthDelta);
			mSignatureBitmapCanvas.drawPoint(x, y, mPaint);
			expandDirtyRect(x, y);
		}

		mPaint.setStrokeWidth(originalWidth);
	}

	private ControlTimedPoints calculateCurveControlPoints(TimedPoint s1, TimedPoint s2, TimedPoint s3) {
		float dx1 = s1.x - s2.x;
		float dy1 = s1.y - s2.y;
		float dx2 = s2.x - s3.x;
		float dy2 = s2.y - s3.y;

		TimedPoint m1 = new TimedPoint((s1.x + s2.x) / 2.0f, (s1.y + s2.y) / 2.0f);
		TimedPoint m2 = new TimedPoint((s2.x + s3.x) / 2.0f, (s2.y + s3.y) / 2.0f);

		float l1 = (float) Math.sqrt(dx1 * dx1 + dy1 * dy1);
		float l2 = (float) Math.sqrt(dx2 * dx2 + dy2 * dy2);

		float dxm = (m1.x - m2.x);
		float dym = (m1.y - m2.y);
		float k = l2 / (l1 + l2);
		TimedPoint cm = new TimedPoint(m2.x + dxm * k, m2.y + dym * k);

		float tx = s2.x - cm.x;
		float ty = s2.y - cm.y;

		return new ControlTimedPoints(new TimedPoint(m1.x + tx, m1.y + ty), new TimedPoint(m2.x + tx, m2.y + ty));
	}

	private float strokeWidth(float velocity) {
		return Math.max(mMaxWidth / (velocity + 1), mMinWidth);
	}

	/**
	 * Called when replaying history to ensure the dirty region includes all
	 * mPoints.
	 *
	 * @param historicalX
	 *            the previous x coordinate.
	 * @param historicalY
	 *            the previous y coordinate.
	 */
	private void expandDirtyRect(float historicalX, float historicalY) {
		if (historicalX < mDirtyRect.left) {
			mDirtyRect.left = historicalX;
		} else if (historicalX > mDirtyRect.right) {
			mDirtyRect.right = historicalX;
		}
		if (historicalY < mDirtyRect.top) {
			mDirtyRect.top = historicalY;
		} else if (historicalY > mDirtyRect.bottom) {
			mDirtyRect.bottom = historicalY;
		}
	}

	/**
	 * Resets the dirty region when the motion event occurs.
	 *
	 * @param eventX
	 *            the event x coordinate.
	 * @param eventY
	 *            the event y coordinate.
	 */
	private void resetDirtyRect(float eventX, float eventY) {

		// The mLastTouchX and mLastTouchY were set when the ACTION_DOWN motion
		// event occurred.
		mDirtyRect.left = Math.min(mLastTouchX, eventX);
		mDirtyRect.right = Math.max(mLastTouchX, eventX);
		mDirtyRect.top = Math.min(mLastTouchY, eventY);
		mDirtyRect.bottom = Math.max(mLastTouchY, eventY);
	}

	private void setIsEmpty(boolean newValue) {
		mIsEmpty = newValue;
		if (mOnSignedListener != null) {
			if (mIsEmpty) {
				mOnSignedListener.onClear();
			} else {
				mOnSignedListener.onSigned();
			}
		}
	}

	private void ensureSignatureBitmap() {
		if (mSignatureBitmap == null) {
			mSignatureBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
			mSignatureBitmapCanvas = new Canvas(mSignatureBitmap);
		}
	}

	private int convertDpToPx(float dp) {
		return Math.round(dp * (getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
	}

	public interface OnSignedListener {
		public void onSigned();

		public void onClear();
	}
}

Style中资源
  <declare-styleable name="SignatureView">
        <attr name="minWidth" format="dimension" />
        <attr name="maxWidth" format="dimension" />
        <attr name="penColor" format="color" />
        <attr name="velocityFilterWeight" format="float" />
    </declare-styleable>

utils中增加工具类

public class Bezier {

	public TimedPoint startPoint;
	public TimedPoint control1;
	public TimedPoint control2;
	public TimedPoint endPoint;

	public Bezier(TimedPoint startPoint, TimedPoint control1, TimedPoint control2, TimedPoint endPoint) {
		this.startPoint = startPoint;
		this.control1 = control1;
		this.control2 = control2;
		this.endPoint = endPoint;
	}

	public float length() {
		int steps = 10, length = 0;
		int i;
		float t;
		double cx, cy, px = 0, py = 0, xdiff, ydiff;

		for (i = 0; i <= steps; i++) {
			t = i / steps;
			cx = point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
			cy = point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
			if (i > 0) {
				xdiff = cx - px;
				ydiff = cy - py;
				length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
			}
			px = cx;
			py = cy;
		}
		return length;

	}

	public double point(float t, float start, float c1, float c2, float end) {
		return start * (1.0 - t) * (1.0 - t) * (1.0 - t) + 3.0 * c1 * (1.0 - t) * (1.0 - t) * t + 3.0 * c2 * (1.0 - t)
				* t * t + end * t * t * t;
	}

}

public class ControlTimedPoints {

	public TimedPoint c1;
	public TimedPoint c2;

	public ControlTimedPoints(TimedPoint c1, TimedPoint c2) {
		this.c1 = c1;
		this.c2 = c2;
	}

}

public class TimedPoint {
	public final float x;
	public final float y;
	public final long timestamp;

	public TimedPoint(float x, float y) {
		this.x = x;
		this.y = y;
		this.timestamp = System.currentTimeMillis();
	}

	public float velocityFrom(TimedPoint start) {
		float velocity = distanceTo(start) / (this.timestamp - start.timestamp);
		if (velocity != velocity)
			return 0f;
		return velocity;
	}

	public float distanceTo(TimedPoint point) {
		return (float) Math.sqrt(Math.pow(point.x - this.x, 2) + Math.pow(point.y - this.y, 2));
	}
}

调用Activity实现签名

public class SignActivity extends AppCompatActivity {

    private String creatTime;
    private Uri contentUri;
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);

    private  String[] res;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign);
        gainPermission();
        gainCurrenTime();
        initView();
    }

    private void gainPermission() {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            if (ContextCompat.checkSelfPermission(SignActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(SignActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 4);
            } else {
                Toast.makeText(this, "已开启权限", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void gainCurrenTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddhhmmss");
        Date curDate = new Date(System.currentTimeMillis());//获取当前时间
        creatTime = formatter.format(curDate);
    }

    private void initView() {
        final Button mClearButton=(Button) findViewById(R.id.clear_button);
        final Button mSaveButton=(Button) findViewById(R.id.save_button);
        final SignatureView mSignaturePad=(SignatureView) findViewById(R.id.signature_pad);
        mSignaturePad.setOnSignedListener(new SignatureView.OnSignedListener() {
            @Override
            public void onSigned() {
                mSaveButton.setEnabled(true);
                mClearButton.setEnabled(true);
            }
            @Override
            public void onClear() {
                mSaveButton.setEnabled(false);
                mClearButton.setEnabled(false);
            }
        });

        mClearButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSignaturePad.clear();
            }
        });
        mSaveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Bitmap signatureBitmap = mSignaturePad.getSignatureBitmap();
                Bitmap bitmap = compressScale(signatureBitmap);
                if (addSignatureToGallery(bitmap)) {
                    Toast.makeText(SignActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(SignActivity.this, "保存失败", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
    public File getAlbumStorageDir(String albumName) {
        File file = new File(Environment.getExternalStorageDirectory(), albumName);
        if (!file.mkdirs()) {
            Log.e("SignaturePad", "Directory not created");
        }
        return file;
    }
    public void saveBitmapToJPG(Bitmap bitmap, File photo) throws IOException {
        Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(newBitmap);
        canvas.drawColor(Color.WHITE);
        canvas.drawBitmap(bitmap, 0, 0, null);
        OutputStream stream = new FileOutputStream(photo);
        newBitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream);
        stream.close();
    }
    public boolean addSignatureToGallery(Bitmap signature) {
        boolean result = false;
        try {
            final File photo = new File(getAlbumStorageDir("draw"), String.format(creatTime+".jpg", System.currentTimeMillis()));
            saveBitmapToJPG(signature,photo);
            contentUri = Uri.fromFile(photo);
            mediaScanIntent.setData(contentUri);
            SignActivity.this.sendBroadcast(mediaScanIntent);
            result = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     *  
     *  * 图片按比例大小压缩方法 
     *  * 
     *  * @param image (根据Bitmap图片压缩) 
     *  * @return 
     *  
     */
    public static Bitmap compressScale(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        if( baos.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        //开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 800f;//这里设置高度为800f
        float ww = 480f;//这里设置宽度为480f
        //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;//be=1表示不缩放
        if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;//设置缩放比例
        //newOpts.inPreferredConfig = Bitmap.Config.RGB_565;//降低图片从ARGB888到RGB565
        //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        isBm = new ByteArrayInputStream(baos.toByteArray());
        bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        // return compressImage(bitmap);//压缩好比例大小后再进行质量压缩
        return  bitmap;
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 4:
                if(grantResults.length>0 &&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    Toast.makeText(this, "已打开权限!", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(this, "请打开权限!", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        /**
         * 设置为横屏
         */
        if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
        super.onResume();
    }
}

布局资源activity_sign

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".SignActivity">

    <LinearLayout
        android:id="@+id/buttons_container"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/clear_button"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="10dp"
            android:layout_weight="1"
            android:enabled="false"
            android:text="清除"
            android:textColor="@color/colorAccent"
            android:textSize="15sp" />

        <Button
            android:id="@+id/save_button"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="20dp"
            android:layout_weight="1"
            android:enabled="false"
            android:text="保存"
            android:textColor="@color/colorAccent"
            android:textSize="15sp" />
    </LinearLayout>

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_above="@+id/buttons_container"
        android:id="@+id/signature_pad_container">

        <FrameLayout
            android:layout_width="fill_parent"
            android:layout_height="1dp"
            android:background="@android:color/darker_gray"
            android:layout_marginBottom="8dp"
            android:layout_above="@+id/signature_pad_description" />

        <TextView
            android:id="@+id/signature_pad_description"
            android:layout_width="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_height="wrap_content"
            android:textColor="@android:color/darker_gray"
            android:layout_marginBottom="16dp"
            android:layout_alignParentBottom="true" />

        <com.mefss.lab.pda.view.SignatureView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:id="@+id/signature_pad" />
    </RelativeLayout>


</LinearLayout>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值