34

アプリの開発を開始しました。昨日メニューを作成しましたが、onClick メソッドが機能しません。View を拡張して彼女の MainMenuObject と呼ばれるクラスを作成しました。このクラスは、メイン メニューの任意のオブジェクト (ボタン、ロゴなど) 用です。メニューの開始時にアニメーションを実行しているため、特別なクラスを作成しました。MainMenuObject クラスを構築した後、View を拡張する別のクラス (OpeningTimesView) を構築しました。このクラスにはメイン メニューのすべてのボタンが含まれ、メイン アクティビティのレイアウトとして機能します。

すべてがうまくいき、アニメーションは非常にうまくいき、ボタンにリスナーを配置したかったので、onClickListener の実装を OpeningTimesView クラスに追加し、onClick メソッドをオーバーライドしました。次に、setOnClickListener(this) と setClickable(true) を使用してリスナーをボタンに追加しましたが、機能しません。私はすべてを試しました!私が間違っていることを理解するのを手伝ってください。「if」に依存しないonClickメソッドにトーストを追加しましたが、どちらも表示されません。

(ところで、すべてのクラスがアクセスできる変数として画面の幅と高さを定義する方法はありますか?表示オブジェクトから高さと幅を取得するため、静的にすることはできませんが、別の方法が必要です)

これはコードです:

public class OpeningTimesView extends View implements OnClickListener{
    private MainMenuObjectView searchButton;
    private MainMenuObjectView supportButton;
    private MainMenuObjectView aboutButton;
    private int screenWidth;
    private int screenHeight;
    public OpeningTimesView(Context context, Display dis) {
        super(context);

        this.screenWidth = dis.getWidth();
        this.screenHeight = dis.getHeight();

        searchButton = new MainMenuObjectView(context, 200, MovingMode.RIGHT, R.drawable.search, dis);
        supportButton = new MainMenuObjectView(context, 400, MovingMode.LEFT, R.drawable.support, dis);
        aboutButton = new MainMenuObjectView(context, 600, MovingMode.RIGHT, R.drawable.about, dis);

        searchButton.setClickable(true);
        supportButton.setClickable(true);
        aboutButton.setClickable(true);

        searchButton.setOnClickListener(this);
        supportButton.setOnClickListener(this);
        aboutButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View view){
        Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        if(view == searchButton){
            Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        }
        else if(view == supportButton){
            Toast.makeText(getContext(), "Support button pressed", Toast.LENGTH_SHORT).show();
        }
        else Toast.makeText(getContext(), "About button pressed", Toast.LENGTH_SHORT).show();
    }
    @Override
    public void onDraw(Canvas canvas)
    {
        // Drawing the buttons
        this.searchButton.onDraw(canvas);
        this.aboutButton.onDraw(canvas);
        this.supportButton.onDraw(canvas);
    }

前もってありがとう、エラド!

4

11 に答える 11

23

同じ問題が発生しました。カスタムビューを作成しましたが、アクティビティに新しいリスナーを登録したときにv.setOnClickListener(new OnClickListener() {...});、リスナーを呼び出しても呼び出されませんでした。

私のカスタムビューでは、public boolean onTouchEvent(MotionEvent event) {...}メソッドも上書きしました。問題は、Viewクラスのメソッドを呼び出さなかったことです- super.onTouchEvent(event)。それで問題は解決しました。したがって、リスナーが呼び出されない理由がわからない場合は、スーパークラスのonTouchEventメソッドを呼び出すのを忘れている可能性があります。

簡単な例を次に示します。

private static class CustomView extends View implements View.OnClickListener {
    public CustomView(Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);   // this super call is important !!!
        // YOUR LOGIC HERE
        return true;
    }

    @Override
    public void onClick(View v) {
        // DO SOMETHING HERE
    }
}
于 2012-01-28T15:33:26.653 に答える
17

UI フレームワークの動作に慣れていない場合、Android でカスタム コントロールを作成するのは難しい場合があります。まだ読んでいない場合は、これらを読むことをお勧めします。

http://developer.android.com/guide/topics/ui/declaring-layout.html

http://developer.android.com/guide/topics/ui/custom-components.html

http://developer.android.com/guide/topics/ui/layout-objects.html

XML でレイアウトを宣言すると、要素がネストされることに注意してください。これにより、Java コードのみを使用してコンポーネントをカスタマイズするときに自分で作成する必要があるレイアウト階層が作成されます。

ほとんどの場合、Android のタッチ階層に巻き込まれています。他の一般的なモバイル プラットフォームとは異なり、Android はビュー階層の最上位から順に配信されるタッチ イベントを配信します。従来、階層の上位レベルを占めていたクラス (Activity と Layouts) には、それ自体が消費しないタッチを転送するためのロジックが含まれています。

OpeningTimesViewそのため、ViewGroup(すべての Android レイアウトのスーパークラス) または特定のレイアウト ( LinearLayoutRelativeLayoutなど) を拡張し、ボタンを子として追加するように変更することをお勧めします。現在、定義された階層はないようです (ボタンはコンテナーに実際に「含まれている」わけではなく、単なるメンバーです)。これにより、イベントが実際にどこに行くのかについて問題が混乱する可能性があります。

  1. タッチがより自然にボタンに流れ、クリック イベントがトリガーされるようにする必要があります。
  2. Android のレイアウト メカニズムを利用してビューを描画することができ、そのすべてを描画コードに依存する必要はありません。

ボタンを最終的な場所に配置するのに役立つレイアウト クラスを選択して開始します。Android のアニメーション フレームワークまたはカスタム描画コード (現在のように) を使用して、その時点まで好きなようにアニメーション化できます。ボタンの位置とそのボタンが現在描画されている場所は、必要に応じて非常に異なることが許可されています。これは、現在のアニメーション フレームワークが Android でどのように機能するか (3.0 より前) ですが、それは別の問題です。またAbsoluteLayout、オブジェクトを好きな場所に配置して置き換えることができる もあります...ただし、これを使用するすべての Android デバイスでアプリがどのように見えるかに注意してください (画面サイズが異なるため)。

表示情報に関する2番目のポイントについて。 最も簡単な方法は、おそらくContext.getResources().getDisplayMetrics()必要な場所で使用することです。 Activityから継承してContextいるため、このメソッドを直接呼び出すことができます。ビューには常に でContextアクセスできる がありますgetContext()。構築時にパラメータとしてを渡すことができるその他のクラス(これは Android の一般的なパターンです。主に にアクセスするために、Context多くのオブジェクトが を必要とすることがわかります)。ContextResources

これは、物事をジャンプスタートするためのスケルトンの例です。これは、最終的な場所として 3 つを水平に並べるだけです。

Public class OpeningTimesView extends LinearLayout implements OnClickListener {
    private MainMenuObjectView searchButton;
    private MainMenuObjectView supportButton;
    private MainMenuObjectView aboutButton;
    private int screenWidth;
    private int screenHeight;

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

    //Thus constructor gets used if you ever instantiate your component from XML
    public OpeningTimesView(Context context, AttributeSet attrs) {
        super(context, attrs);

        /* This is a better way to obtain your screen info
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        screenWidth = display.widthPixels;
        screenHeight = display.heightPixels;
        */
        //This way works also, without needing to customize the constructor
        WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Display dis = wm.getDefaultDisplay();
        screenWidth = dis.getWidth();
        screenHeight = dis.getHeight();

        searchButton = new MainMenuObjectView(context, 200, MovingMode.RIGHT, R.drawable.search, dis);
        supportButton = new MainMenuObjectView(context, 400, MovingMode.LEFT, R.drawable.support, dis);
        aboutButton = new MainMenuObjectView(context, 600, MovingMode.RIGHT, R.drawable.about, dis);

        //Even if they don't extend button, if MainMenuObjectView is always clickable
        // this should probably be brought into that class's constructor
        searchButton.setClickable(true);
        supportButton.setClickable(true);
        aboutButton.setClickable(true);

        searchButton.setOnClickListener(this);
        supportButton.setOnClickListener(this);
        aboutButton.setOnClickListener(this);

        //Add the buttons to the layout (the buttons are now children of the container)
        setOrientation(LinearLayout.HORIZONTAL);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        addView(searchButton, params);
        addView(supportButton, params);
        addView(aboutButton, params);       
    }

    @Override
    public void onClick(View view){
        Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        if(view == searchButton){
            Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        }
        else if(view == supportButton){
            Toast.makeText(getContext(), "Support button pressed", Toast.LENGTH_SHORT).show();
        }
        else Toast.makeText(getContext(), "About button pressed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDraw(Canvas canvas)
    {
        //Drawing the buttons
        // This may only be necessary until they are in place, then just call super.onDraw(canvas)
        this.searchButton.onDraw(canvas);
        this.aboutButton.onDraw(canvas);
        this.supportButton.onDraw(canvas);
    }    
}

そこからカスタマイズできます。おそらく、描画コードまたはカスタム Animation オブジェクトを使用してボタンをアニメーション化するまで View.INVISIBLE に設定された可視性でボタンを開始し、最終的な静止位置でそれらを可視化します。

ただし、ここで重要なのは、タッチ イベントを受け取ったときに対応する子に転送する必要があることを認識できるほど、レイアウトがスマートであることです。これがなくてもカスタム ビューを作成できますが、コンテナのすべてのタッチをインターセプトし、イベントを手動で転送するサブビューを決定するために計算を行う必要があります。本当にレイアウト マネージャーを機能させることができない場合は、これが頼りになります。

それが役立つことを願っています!

于 2011-04-18T13:43:24.860 に答える
9

私はこれを行います:

public class YourView extends LinearLayout implements OnClickListener {
    OnClickListener listener;

    //... constructors

    public void setOnClickListener(OnClickListener listener) {
        this.listener = listener;
    }

    @Override
    public void onClick(View v) {
        if (listener != null)
             listener.onClick(v);
    }
}
于 2016-09-01T11:53:58.053 に答える
9

コンストラクターを呼び出して、自分自身setOnClickListener(this)に実装する必要がありView.OnClickListenerます。

この上:

public class MyView extends View implements View.OnClickListener {

    public MyView(Context context) {
        super(context);
        setOnClickListener(this);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Toast.makeText(getContext(), "On click.", Toast.LENGTH_SHORT).show();
    }
}
于 2017-02-23T22:10:03.400 に答える
6

私も同じ問題を抱えていました。私の場合、カスタム ビューのルート要素として LinearLayout を使用し、 clickableandfocusableを true に設定し、カスタム ビュー タグ自体 (フラグメントのレイアウトで使用) もclickableandに設定しましたfocusable。それを機能させるために私がしなければならなかった唯一のことは、XML内からすべてのclickableandfocusable属性を削除することでした:)直観に反しますが、うまくいきました。

于 2018-11-21T16:12:38.763 に答える
3

これらはクリックに応答するオブジェクトであるためonClickListener、クラスに を実装します。 別の代替手段は、そこにあるボタンのみを使用しているため、の代わりに拡張することですMainMenuObjectView
ButtonView


更新:完全な例


これは、クリック可能なビューに直接実装するという考えです。必要に応じて をTestView拡張Viewおよびオーバーライドし、クリックにも応答するクラスがあります。onDrawアニメーションの実装は省略しました。その部分があり、ClickListener議論には関係ありません。
Eclair エミュレーターでテストしたところ、期待どおりに動作しました (クリック後のトースト メッセージ)。

ファイル: Test.java

package com.aleadam.test;

import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;

public class Test extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout ll = new LinearLayout(this);
        ll.setOrientation(LinearLayout.VERTICAL);
        TextView label = new TextView(this);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
             LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        label.setText("Click the circle!");
        TestView testView = new TestView(this);
        ll.addView(label, layoutParams);
        ll.addView(testView, layoutParams);
        setContentView(ll);
    }
}

ファイル: TestView.java

package com.aleadam.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

public class TestView extends View implements OnClickListener {
    Context context;

    public TestView(Context context) {
        super(context);
        this.context = context;
        setOnClickListener(this);
    }

    public void onClick(View arg0) {
        Toast.makeText(context, "View clicked.", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDraw (Canvas canvas) {
        super.onDraw(canvas);
        this.setBackgroundColor(Color.LTGRAY);
        Paint paint = new Paint (Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.RED);
        canvas.drawCircle(20, 20, 20, paint);
    }
}

クリック可能なものとクリックできないものが必要な場合は、ブーリアン引数を持つコンストラクターを追加して、ClickListener がビューにアタッチされているかどうかを判断できます。

public TestView(Context context, boolean clickable) {
    super(context);
    this.context = context;
    if (clickable)
        setOnClickListener(this);
}
于 2011-04-18T13:44:23.520 に答える
3

私は解決策を持っています!これは、この特定の問題に対する解決策ではなく、まったく新しいアプローチです。このスレッドを知っている人に送ったところ、Android が持つアニメーション SDK (前述のワイヤレス デザインなど) を使用するように言われたので、4 つのクラスでメイン メニュー ページを作成する代わりに、拡張する 1 つのクラスだけで作成しています。 Activity であり、Animation クラスは多くのアニメーション オプションを提供します。私を助けてくれてありがとう、あなたは素晴らしいです。誰かが同じ問題または何かでこのスレッドに遭遇した場合、コードを追加しています:

package elad.openapp;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.Toast;

public class OpeningTimes extends Activity implements OnClickListener{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    // Disabling the title bar..
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.main);

    // Create the buttons and title objects
    ImageView title = (ImageView)findViewById(R.id.title_main);
    ImageView search = (ImageView)findViewById(R.id.search_button_main);
    ImageView support = (ImageView)findViewById(R.id.support_button_main);
    ImageView about = (ImageView)findViewById(R.id.about_button_main);

    // Setting the onClick listeners
    search.setOnClickListener(this);
    support.setOnClickListener(this);
    about.setOnClickListener(this);

    setButtonsAnimation(title, search, support, about);

}

@Override
public void onClick(View v) {
    if(v.getId()==R.id.search_button_main){
        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        startActivity(new Intent(this,SearchPage.class));
    }
    else if(v.getId()==R.id.support_button_main){
        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        Toast.makeText(this, "Coming soon...", Toast.LENGTH_LONG).show();
    }
    else if(v.getId()==R.id.about_button_main){

        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        Toast.makeText(this, "Coming soon...", Toast.LENGTH_LONG).show();
    }
}

// Setting the animation on the buttons
public void setButtonsAnimation(ImageView title, ImageView search, ImageView support, ImageView about){
    // Title animation (two animations - scale and translate)
    AnimationSet animSet = new AnimationSet(true);

    Animation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f);
    anim.setDuration(750);
    animSet.addAnimation(anim);

    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);
    animSet.addAnimation(anim);

    title.startAnimation(animSet);

    // Search button animation
    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1.5f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);

    search.startAnimation(anim);

    // Support button animation
    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1.5f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);

    support.startAnimation(anim);

    // About button animation
    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 3f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);

    about.startAnimation(anim);
}
}
于 2011-04-19T21:10:56.543 に答える