将自定义绘图视图 (canvas) 放入扩展 Fragment 的 class 中
Put a custom Drawing View (canvas) inside a class that extends Fragment
我需要创建一个 "page" 可以用手指画画的地方。我在 Whosebug 上找到了如何做,但我不想创建一个新的 activity,我想像下面这样思考,这可能吗?
扩展框架的class布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<EditText
android:id="@+id/note_draw_titolo"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:padding="8dp"
android:hint="Titolo"
android:textAlignment="center"
android:textStyle="bold"
style="@style/Base.TextAppearance.AppCompat.Body2"/>
</android.support.v7.widget.CardView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<com.marcocreation.********.DrawingView
android:id="@+id/drawView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:visibility="visible"
android:layout_alignParentBottom="true">
</com.marcocreation.*******.DrawingView>
</RelativeLayout>
绘图视图class:
public class DrawingView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
private Paint mPaint;
public DrawingView(Context c, AttributeSet attrs, int defStyle) {
super(c, attrs, defStyle);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
initPaint();
}
protected void initPaint(){
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath( mPath, mPaint);
canvas.drawPath( circlePath, circlePaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
在 customFragment 中,我从以下内容开始:
public class NoteFragment extends Fragment {
private EditText sharedTitolo;
private EditText sharedNota;
private NoteDataSource notesource;
private TextView recordTimeText;
private ImageButton audioSendButton;
private View recordPanel;
private View slideText;
private Context context;
private DrawingView drawView;
public static NoteFragment createInstance(int index, int tipo, Context c) {
NoteFragment noteFragment = new NoteFragment(c);
Bundle bundle = new Bundle();
...
return noteFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@SuppressLint("ValidFragment")
public NoteFragment (Context c){
this.context = c;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = null;
Bundle bundle = getArguments();
switch (bundle.getInt("type")){
case 1:
...
break;
case 2:
view = (View) inflater.inflate(R.layout.note_draw_layout, container, false);
sharedTitolo = (EditText)view.findViewById(R.id.note_draw_titolo);
drawView = (DrawingView)view.findViewById(R.id.drawView);
if(bundle.getInt("index")>0){
Log.i("nf_indice",""+bundle.getInt("index"));
sharedTitolo.setText("text");
}
else{
...
}
break;
case 3:
...
break;
default:
break;
}
return view;
}
}
但是我得到这个错误:
android.view.InflateException: Binary XML file line #32: Binary XML file line #32: Error inflating class com.marcocreation.******.DrawingView
ps Notefragment 由扩展 appcompactactivity 并创建 FragmentPagerAdapter 的 class 启动,它有一个协调器布局。
完整的错误日志:
07-24 11:03:44.805 29830-29830/com.marcocreation.******* E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.marcocreation.*******, PID: 29830
android.view.InflateException: Binary XML file line #28: Binary XML file line #28: Error inflating class com.marcocreation.*******.DrawingView
at android.view.LayoutInflater.inflate(LayoutInflater.java:539)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at com.marcocreation.*******.NoteFragment.onCreateView(NoteFragment.java:86)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2074)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1286)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:758)
at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:1632)
at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:637)
at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:143)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1235)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1083)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1609)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:669)
at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:89)
at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1319)
at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:734)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:669)
at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:736)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
at android.view.View.measure(View.java:18794)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1216)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:606)
为了将来参考,这里是有效的解决方案。
在inflation期间调用的View
构造函数是View(Context context, AttributeSet attrs)
,但是在上面的DrawingView
中没有提供这样的构造函数。因此 Android 系统无法扩充布局文件,因为它找不到合适的构造函数。
以下 DrawingView
构造函数应该可以解决问题:
// Constructor called during inflation
public DrawingView(Context c, AttributeSet attrs) {
this(c, attrs, 0);
}
public DrawingView(Context c, AttributeSet attrs, int defStyle) {
super(c, attrs, defStyle);
// ...
}
我需要创建一个 "page" 可以用手指画画的地方。我在 Whosebug 上找到了如何做,但我不想创建一个新的 activity,我想像下面这样思考,这可能吗?
扩展框架的class布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<EditText
android:id="@+id/note_draw_titolo"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:padding="8dp"
android:hint="Titolo"
android:textAlignment="center"
android:textStyle="bold"
style="@style/Base.TextAppearance.AppCompat.Body2"/>
</android.support.v7.widget.CardView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<com.marcocreation.********.DrawingView
android:id="@+id/drawView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:visibility="visible"
android:layout_alignParentBottom="true">
</com.marcocreation.*******.DrawingView>
</RelativeLayout>
绘图视图class:
public class DrawingView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
private Paint mPaint;
public DrawingView(Context c, AttributeSet attrs, int defStyle) {
super(c, attrs, defStyle);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
initPaint();
}
protected void initPaint(){
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath( mPath, mPaint);
canvas.drawPath( circlePath, circlePaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
在 customFragment 中,我从以下内容开始:
public class NoteFragment extends Fragment {
private EditText sharedTitolo;
private EditText sharedNota;
private NoteDataSource notesource;
private TextView recordTimeText;
private ImageButton audioSendButton;
private View recordPanel;
private View slideText;
private Context context;
private DrawingView drawView;
public static NoteFragment createInstance(int index, int tipo, Context c) {
NoteFragment noteFragment = new NoteFragment(c);
Bundle bundle = new Bundle();
...
return noteFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@SuppressLint("ValidFragment")
public NoteFragment (Context c){
this.context = c;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = null;
Bundle bundle = getArguments();
switch (bundle.getInt("type")){
case 1:
...
break;
case 2:
view = (View) inflater.inflate(R.layout.note_draw_layout, container, false);
sharedTitolo = (EditText)view.findViewById(R.id.note_draw_titolo);
drawView = (DrawingView)view.findViewById(R.id.drawView);
if(bundle.getInt("index")>0){
Log.i("nf_indice",""+bundle.getInt("index"));
sharedTitolo.setText("text");
}
else{
...
}
break;
case 3:
...
break;
default:
break;
}
return view;
}
}
但是我得到这个错误:
android.view.InflateException: Binary XML file line #32: Binary XML file line #32: Error inflating class com.marcocreation.******.DrawingView
ps Notefragment 由扩展 appcompactactivity 并创建 FragmentPagerAdapter 的 class 启动,它有一个协调器布局。
完整的错误日志:
07-24 11:03:44.805 29830-29830/com.marcocreation.******* E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.marcocreation.*******, PID: 29830
android.view.InflateException: Binary XML file line #28: Binary XML file line #28: Error inflating class com.marcocreation.*******.DrawingView
at android.view.LayoutInflater.inflate(LayoutInflater.java:539)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at com.marcocreation.*******.NoteFragment.onCreateView(NoteFragment.java:86)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2074)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1286)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:758)
at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:1632)
at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:637)
at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:143)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1235)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1083)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1609)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:669)
at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:89)
at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1319)
at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:734)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:669)
at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:736)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18794)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
at android.view.View.measure(View.java:18794)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1216)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:606)
为了将来参考,这里是有效的解决方案。
在inflation期间调用的View
构造函数是View(Context context, AttributeSet attrs)
,但是在上面的DrawingView
中没有提供这样的构造函数。因此 Android 系统无法扩充布局文件,因为它找不到合适的构造函数。
以下 DrawingView
构造函数应该可以解决问题:
// Constructor called during inflation
public DrawingView(Context c, AttributeSet attrs) {
this(c, attrs, 0);
}
public DrawingView(Context c, AttributeSet attrs, int defStyle) {
super(c, attrs, defStyle);
// ...
}