Android - 如何选择上下文?

时间:2014-12-01 18:48:43

标签: java android android-fragments android-canvas

手指绘画程序存在问题。在这个程序中,我实现了两个片段,我已经意识到我的绘图片段,包含画布和支持绘图,不显示画布,还有其他问题。我的意识与我注意到,当我编写一个新类来扩展View时(因为片段不支持扩展视图本身),我没有实例化该类。我在片段中创建了一个类的实例,但是这个实例需要一个Context作为参数,而且我不确定要使用哪个上下文。我尝试过使用' getActivity()'作为上下文,但这不起作用,使程序挂起(在调试中)或崩溃(在正常模式下)。我应该在这个类实例化的上下文中使用什么?

片段代码:

package com.example.chris.drawingtest;

import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.LinearLayout;

import com.example.chris.drawingtest.R;

/**
 * Created by Chris on 11/28/2014.
 */
public class DrawingFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d("onCreateView: ","This ran successfully");
        return inflater.inflate(R.layout.fragment_drawing, container, false);
    }

    private class DrawView extends View {

        private Path drawpath = new Path();
        private Paint drawpaint = new Paint();
        private Paint canvaspaint;
        private Canvas drawcanvas;
        private Bitmap canvasBitmap;

        private int paintColor = 0xFF000000;    //opaque black for pencil
        private int canvasColor = 0xFFFFFFFF;   //pure white for canvas

        public DrawView(Context context) {
            super(context);

            Log.d("DrawView: ", "method is called");

            drawpaint.setColor(paintColor);

            drawpaint.setStrokeWidth(20);
            drawpaint.setStyle(Paint.Style.STROKE);
            drawpaint.setStrokeJoin(Paint.Join.ROUND);
            drawpaint.setStrokeCap(Paint.Cap.ROUND);

            canvaspaint = new Paint(Paint.DITHER_FLAG);

            Point p = getScreenSize(context);
            int w = p.x;
            int h = p.y;

            OnSizeChanged(w,h,0,0);

        }

        protected Point getScreenSize(Context context) {
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            Display display = wm.getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            return size;
        }

        protected void OnSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w,h,oldw,oldh);

            Log.d("OnSizeChanged: ", "method is called");

            canvasBitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
            drawcanvas = new Canvas(canvasBitmap);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            Log.d("onDraw: ", "method is called");

            drawcanvas.drawBitmap(canvasBitmap,0,0,canvaspaint);
            canvas.drawPath(drawpath, drawpaint);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float touchX = event.getX();
            float touchY = event.getY();

            Log.d("onTouchEvent: ", "method is called");

            switch(event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    drawpath.moveTo(touchX,touchY);
                    break;
                case MotionEvent.ACTION_MOVE:
                    drawpath.lineTo(touchX,touchY);
                    break;
                case MotionEvent.ACTION_UP:
                    drawcanvas.drawPath(drawpath, drawpaint);
                    drawpath.reset();
                    break;
                default:
                    return false;
            }
            invalidate();
            return true;
        }
    }

    //private DrawView drawView = new DrawView();    this is the problematic instantiation

}

编辑:我想添加一些新内容以反映给出的答案。

按照Bruce提出的想法,我扩展了DrawingFragment的XML文件。不幸的是,这导致了进一步的崩溃,毫无疑问,我自己的一些天真应该受到指责。任何人都可以就此问题分享一些见解吗?

logcat的:

12-01 15:38:50.116    8045-8045/com.example.chris.drawingtest D/onCreateView:﹕ This ran successfully
12-01 15:38:50.126    8045-8045/com.example.chris.drawingtest D/AndroidRuntime﹕ Shutting down VM
12-01 15:38:50.126    8045-8045/com.example.chris.drawingtest W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41c9aba8)
12-01 15:38:50.126    8045-8045/com.example.chris.drawingtest E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.example.chris.drawingtest, PID: 8045
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.chris.drawingtest/com.example.chris.drawingtest.DrawingActivity}: android.view.InflateException: Binary XML file line #11: Error inflating class com.example.chris.drawingtest.DrawingFragment$DrawView
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
            at android.app.ActivityThread.access$800(ActivityThread.java:135)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: android.view.InflateException: Binary XML file line #11: Error inflating class com.example.chris.drawingtest.DrawingFragment$DrawView
            at android.view.LayoutInflater.createView(LayoutInflater.java:603)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:696)
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
            at com.example.chris.drawingtest.DrawingFragment.onCreateView(DrawingFragment.java:32)
            at android.app.Fragment.performCreateView(Fragment.java:1700)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:866)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1040)
            at android.app.FragmentManagerImpl.addFragment(FragmentManager.java:1142)
            at android.app.Activity.onCreateView(Activity.java:4786)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:689)
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
            at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
            at android.app.Activity.setContentView(Activity.java:1929)
            at com.example.chris.drawingtest.DrawingActivity.onCreate(DrawingActivity.java:18)
            at android.app.Activity.performCreate(Activity.java:5231)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2148)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
            at android.app.ActivityThread.access$800(ActivityThread.java:135)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
            at java.lang.Class.getConstructorOrMethod(Class.java:472)
            at java.lang.Class.getConstructor(Class.java:446)
            at android.view.LayoutInflater.createView(LayoutInflater.java:568)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:696)
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
            at com.example.chris.drawingtest.DrawingFragment.onCreateView(DrawingFragment.java:32)
            at android.app.Fragment.performCreateView(Fragment.java:1700)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:866)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1040)
            at android.app.FragmentManagerImpl.addFragment(FragmentManager.java:1142)
            at android.app.Activity.onCreateView(Activity.java:4786)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:689)
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
            at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
            at android.app.Activity.setContentView(Activity.java:1929)
            at com.example.chris.drawingtest.DrawingActivity.onCreate(DrawingActivity.java:18)
            at android.app.Activity.performCreate(Activity.java:5231)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2148)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
            at android.app.ActivityThread.access$800(ActivityThread.java:135)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)

片段XML:

<?xml version="1.0" encoding="utf-8"?>

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

    <view
        xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.example.chris.drawingtest.DrawingFragment$DrawView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"


        />

</LinearLayout>

2 个答案:

答案 0 :(得分:2)

为什么要在名称恰当的View之外创建自定义onCreateView?或许试一试:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    Log.d("onCreateView: ","This ran successfully");
    View v = inflater.inflate(R.layout.fragment_drawing, container, false);
    drawView = new DrawView(v.getContext());
    //and then add drawView wherever you want it within fragment_drawing
    //((ViewGroup)v).addView(drawView); *change this to where you want the view and with the appropriate casting and layout parameters*
    return v;
}

答案 1 :(得分:2)

这里有一些问题。

1)您已将View子类作为Fragment的内部类,这意味着每个构造函数都有一个隐含的DrawingFragment参数。这意味着您的View没有Android从XML构造Views所需的标准构造函数。您应该将内部类设置为静态,或者将其放在自己的文件中。

2)你有OnSizeChanged(上限O),它不会覆盖小写版本,然后你调用超级版本。我看到你是从你的构造函数中调用你的版本,但如果Android出于某种原因调整你的视图大小,你就不会听到它。

3)您可能希望跟踪画布的当前大小,因此每次调用onSizeChanged时都可以避免重新创建它。他们可能已经解决了这个问题,但在过去,我已经看到过多次调用此函数,大小相同。

4)您确定要使用画布的屏幕尺寸吗?这将包括您无法实际绘制的区域。最好将你的片段和你的视图作为match_parent的宽度和高度,然后等待Android在onSizeChanged中告诉你你的大小。如果您在片段的布局文件R.layout.fragment_drawing(这是您应该做的)中有DrawView,那么您可以在那里设置match_parent属性。

5)说到属性,当Android从XML中膨胀视图时,它们使用与仅上下文版本不同的构造函数。我会为您的自定义视图推荐以下常用模式:

public DrawView(Context context) {
    this(context, null);
}

public DrawView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public DrawView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    //TODO your constructor body here
}

修改

回答有关如何实例化自定义视图的问题。听起来你的DrawView占用了你的整个片段,所以我会这样写。但是,你当然也可以像RelativeLayout这样的父视图作为文件的根目录,并使DrawView成为子视图。另一个影响答案的是DrawView是否仍然是片段的内部类。更简单的情况是DrawView是否在自己的文件中。然后你可以这样做:

<com.example.chris.drawingtest.DrawView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

如果您将DrawView保持为静态内部类,那么您使用&#39; $&#39;分隔符而不是&#39;。但是,&#39; $&#39;是XML的特殊字符,因此您无法在标记中使用它。但是,您可以将类名指定为视图标记的XML属性。像这样:

<view
    xmlns:android="http://schemas.android.com/apk/res/android"
    class="com.example.chris.drawingtest.DrawingFragment$DrawView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

编辑:

是的,这次崩溃正是我在上面#1和#5中讨论的问题。异常java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]告诉您Android无法找到一个View构造函数,它接受Context和AttributeSet(我展示的3个构造函数中的第2个)。确保您的DrawView类是公共的,静态的,并且包含所有构造函数。

public class DrawingFragment extends Fragment {
    public static class DrawView extends View {
    ... // 3 constructors shown above

编辑:

由于您希望将绘制片段放在另一个片段的顶部并占据屏幕的大部分,我会使用RelativeLayout作为您活动的根视图,然后将您的较小的底部视图放在下面,使用wrap_content高度,然后你上面的绘制片段。您可以使用RelativeLayout参数将片段锚定到活动的顶部和底部。所以layout / draw_activity.xml:

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

    <fragment
        android:id="@+id/tools_fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        class="com.example.chris.drawingtest.ToolsFragment" />

    <fragment
        android:id="@+id/draw_fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_above="@id/tools_fragment"
        class="com.example.chris.drawingtest.DrawingFragment" />

</RelativeLayout>

然后假设您的DrawView填充了您的DrawingFragment,您可以使用我已经显示的XML。