java - Android - 如何选择上下文?

标签 java android android-fragments android-canvas

我的手指绘画程序有问题。在这个程序中,我实现了两个 fragment ,我开始意识到我的绘图 fragment (包含 Canvas 并支持在其上绘图)没有显示 Canvas ,并且还有其他问题。我的认识与我注意到虽然我编写了一个新类来扩展 View(这样做是因为 fragment 不支持扩展 View 本身),但我没有实例化该类。我在 fragment 中创建了一个类的实例,但这个实例需要一个上下文作为参数,我不确定要使用什么上下文。我尝试使用“getActivity()”作为上下文,但这不起作用并导致程序挂起(在调试中)或崩溃(在正常模式下)。对于此类实例化的上下文,我应该使用什么?

fragment 代码:

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

}

编辑:我想添加一些新内容来反射(reflect)给出的答案。

按照 Bruce 提出的想法,我扩展了 DrawingFragment 的 XML 文件。不幸的是,这导致了进一步的崩溃,这无疑要归咎于我自己的一些天真。谁能就此问题分享一些见解?

日志:

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)

fragment 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>

最佳答案

这里有几个问题。

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

2) 你有 OnSizeChanged (cap O),它不会覆盖小写版本,然后你调用 super 的版本。我看到您正在从您的构造函数调用您的版本,但如果 Android 出于某种原因调整您的 View 大小,您将不会听到它。

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

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

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

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
}

编辑

回答有关如何实例化自定义 View 的问题。听起来你的 DrawView 占据了你的整个 fragment ,所以我会这样写。但是,您当然也可以将像 RelativeLayout 这样的父 View 作为文件的根,并让 DrawView 成为 subview 。影响答案的另一件事是 DrawView 是否仍然是 fragment 的内部类。更简单的情况是 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 保留为静态内部类,则使用“$”分隔符而不是“.”。但是,'$' 是 XML 的特殊字符,因此您不能在您的标记中使用它。但是,您可以将类名指定为 View 标记的 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 无法找到采用 Context 和 AttributeSet 的 View 构造函数(我展示的 3 个构造函数中的第 2 个)。确保您的 DrawView 类是公共(public)的、静态的,并且包含所有构造函数。

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

编辑:

由于您希望绘制 fragment 位于另一个 fragment 之上,并占据大部分屏幕,我会使用 RelativeLayout 作为 Activity 的 Root View ,然后将较小的底部 View 放在下一个,wrap_content 高度,然后你的绘图 fragment 在上面。您可以使用 RelativeLayout 参数将 fragment 锚定到 Activity 的顶部和底部。所以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。

关于java - Android - 如何选择上下文?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27235111/

相关文章:

java - 有更简单有效的方法来做到这一点吗?

java - 如何将修改后的 GPL 软件的源代码与闭源软件正确 bundle 并发布

android - 寻找 : Android Compass API

Android AppcompatActivity 与 fragment 从后台调用后崩溃(需要等待几分钟)

android - 如何动态设置orderInCategory?

java - fragment Z 顺序在方向改变时改变

java - 从另一个线程访问对象

java - 对 Android 应用程序进行逆向工程时,Smali 代码与 Java 源代码的对比

java - 在 java 中格式化( pretty-print )xml CDATA 值的内容

java - 如何在 GWT 中将自定义 HTML 元素注册为小部件