Каков наилучший способ создания экземпляров виджетов, которые предварительно определены в макетах XML в пользовательском классе View? - PullRequest
1 голос
/ 30 июля 2011

Я довольно новичок в программировании на Android в целом, и у меня возникли особые трудности с перемешиванием пользовательского интерфейса xml / java ... У меня есть макет, который я хотел бы использовать в качестве представления, отображаемого при создании экземпляра пользовательского класса представления в класс деятельности. Это прекрасно работает, просто позвонив

setContentView(R.layout.mylayout) ;

в действии или из пользовательского класса представления через дескриптор к действию. Проблема возникает, когда я хочу взаимодействовать с виджетами на макете - я попытался получить ручку на кнопках с

myButton = (Button) findViewById(R.id.mybuttonid);

и отдельно с

Button myButton = new Button(contextHandle);
myButton = (Button) findViewById(R.layout.mybuttonid);

но в обоих случаях всякий раз, когда я пытаюсь вызвать какие-либо методы из предполагаемого объекта myButton, я получаю исключение NullPointerException в отчете logcat; очевидно, что myButton не создан должным образом ни в одном из приведенных выше случаев. Как правильно создать экземпляры компонентов представления в подобном случае, который объединяет XML и Java, чтобы они могли динамически вызывать методы?

спасибо, CCJ

РЕДАКТИРОВАТЬ: Спасибо всем за ответы, но я думаю, что до 01.08.2011 совет был в основном ориентирован на реализацию, где виджеты должны быть созданы в классе деятельности; Я хочу создать экземпляры виджетов из макета xml в пользовательском классе представления - классе, полностью отделенном от класса действия, который расширяет View и реализует собственный интерфейс OnClickListener. Ниже мой код:

Класс MyActivity:

package com.ccg.myactivity;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RadioButton;

public class MyActivity extends Activity implements OnClickListener {
private boolean touched = false;
private RadioButton myRB;
private Button runB;
private CustomView myView; 



/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mainlayout);
    myRB = (RadioButton) findViewById(R.id.testrb);
    runB = (Button) findViewById(R.id.goButton);
    //set onClick listeners for activity class
    runB.setOnClickListener(this);
}
public void onResume(){
    super.onResume();

}

public void onClick(View v) {
    // do something when the button is clicked
    if (myRB.isChecked()){
        setContentView(R.layout.mylayout);
        myView = new CustomView(this,this); //passing in activity and context 
//handles to custom View class
        //myView.getAnotherB().setOnClickListener(this); //commented out as we 
//don't want to register the custom view's button with the Activty class's 
//OnClickListener; instead it should be registered with the custom View class's own 
//OnClickListener implementation.

    }

   else{
     Log.d("me","alt click");
   }

}


}

Класс CustomView:

package com.ccg.myactivity;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import android.view.View.OnClickListener;

public class CustomView extends View implements OnClickListener{

private Button anotherB;

private Context contextHandle;
private Activity actHandle;

public CustomView(Context context, Activity act) {
    super(context);
    contextHandle = context;
    actHandle = act;
    //anotherB = new Button(contextHandle); //this shouldn't be necessary for 
//instantiation from XML widget
    initCustomView();

}

public void initCustomView(){

    anotherB = (Button) findViewById(R.id.nextbutton);
    anotherB.setOnClickListener(this);

}


public Button getAnotherB(){
    return anotherB;
}


@Override
public void onClick(View arg0) {
    // TODO Auto-generated method stub

    Log.d("me", "Got the custom click!");
}




}

mainlayout.xml, из которого сделан вид по умолчанию:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/widget474"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical">
<RadioGroup android:id="@+id/widget30" android:orientation="horizontal"   
android:layout_x="2dip" android:layout_y="57dip" android:layout_width="match_parent" 
android:layout_height="wrap_content">
<RadioButton android:layout_height="wrap_content" android:id="@+id/testrb"  
android:textSize="15sp" android:text="Run" android:layout_width="wrap_content" 
android:textColor="#ffff99ff"></RadioButton>
</RadioGroup>
<Button android:layout_width="wrap_content" android:text="@string/RUN"  
android:id="@+id/goButton" android:layout_height="wrap_content" 
android:layout_x="222dip" android:layout_y="110dip"></Button>
</LinearLayout>

mylayout.xml, из которого создается макет пользовательского представления:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/widget0"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical">

<Button android:id="@+id/nextbutton" android:layout_height="wrap_content"   
android:layout_width="wrap_content" android:text="work!!!"
> 
</Button>

</LinearLayout>

хорошо, если кто-нибудь может объяснить, почему какой-либо метод вызывает из объекта кнопки anotherB (anotherB.setOnClickListener (this) выше), но также и более простой anotherB.bringToFront ()), вызывающий принудительное закрытие и исключение nullpointerexception в logcat с вышеуказанной реализацией Я был бы очень признателен. Спасибо! CCJ

Ответы [ 4 ]

1 голос
/ 30 июля 2011

Это иногда случается со мной, когда импорт некорректен. Иногда Eclipse будет заполнять импорт как:

import android.R;

конечно, это никогда не найдет ваш ID. У вас не должно быть импорта или что-то вроде

import com.myco.mytestapp.R;

Если вы сделаете это, то первый способ сделать это будет правильным:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mylayout);

    Button b = (Button) findViewById(R.id.mybutton);
}  
1 голос
/ 30 июля 2011

Я бы объявил вашу кнопку вне onCreate без параметра contextHandle ... Контекст будет встроен в вашу кнопку при создании экземпляра (насколько я понимаю).

попробовать:

class YOUR_CLASS {
  Button myButton;

  onCreate() {
    myButton = (Button) findViewById(R.id.WHATEVER_YOU_CALLED_IT_IN_XML);

тогда вы можете установить onClickListener или другие способности (вы можете гуглить это, это просто)

    myButton.setOnClickListener(myOnClickListener);
    myButton.setText("click me!");
  }
}
0 голосов
/ 06 октября 2011

Хорошо, наконец-то появилось время вернуться к этой проблеме, и я думаю, что нашел ответ: во-первых, перед тем, как макет xml или его компоненты могут быть решены, их нужно надуть.Я знал это, но я не был уверен, когда именно они были раздуты.Оказывается, что setContextView (и, вероятно, addContextView) запускают инфляцию XML.Чтобы иметь полностью модульные классы действий / представлений, мне нужно было сделать что-то вроде следующего:

Класс действий -

package com.ai.ultimap;

import com.ai.ultimap.views.HomeView;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup.LayoutParams;

public class UltiMapActivity extends Activity {
private View hv;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    hv = new HomeView(this);
}
}

Класс пользовательских представлений -

package com.ai.ultimap.views;

import com.ai.ultimap.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.*;
import android.widget.*;
import android.view.View.OnClickListener;

public class HomeView extends View implements OnClickListener{

private RadioButton twodRB;
private RadioButton threedRB;
private TextView locTV;
private EditText editlocET;

public HomeView(Activity hAct) {
super(hAct);
    //THE FOLLOWING LINE INFLATES-- IT (or another function which calls xml inflation) 
    //MUST COME BEFORE ANY JAVA ADDRESSING OF WIDGETS IN
    //THE XML LAYOUT
    //Also note that even though you could invoke findViewById from a class extending 
    //View, in this case you must use hAct.findViewById.  I believe this is due to the
    //fact that the activity referenced by hAct is the object responsible for inflating
    //the xml and thus the widgets need to be instantiated from it.  
hAct.setContentView(R.layout.ultimap);
twodRB = (RadioButton) hAct.findViewById(R.id.twodRBV);
threedRB = (RadioButton) hAct.findViewById(R.id.threedRBV);
locTV = (TextView) hAct.findViewById(R.id.locationTV);
editlocET = (EditText) hAct.findViewById(R.id.locationETV);
    //After instantiation however they can be freely accessed from java in 
    //non-activity classes, which is the point; see the next line...
twodRB.setOnClickListener(this);

}

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
locTV.setText("yo");
}

}

Этот код работает должным образом, чтобы загрузить предопределенное представление XML ultimap.xml, а затем динамически обращаться к виджетам из Java (полностью за пределами класса активности), изменяя текст представления текста местоположения с «Location» на «yo»когда нажата радиокнопка twodRB!

Надеюсь, это поможет некоторым гуглерам:)

-CCJ

0 голосов
/ 03 августа 2011

Хорошо, благодаря совету от группы разработчиков Google Android, я думаю, что нашел ответ, по крайней мере, на самую насущную проблему (NPE и принудительное закрытие):

Мне нужно переопределить onFinishInflate в моем пользовательском классе View; именно в этот момент мои дочерние представления XML-макета (например, anotherB) действительно созданы. Класс теперь выглядит так

package com.ccg.myactivity;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import android.view.View.OnClickListener;

public class CustomView extends View implements OnClickListener{

private Button anotherB;

private Context contextHandle;
private Activity actHandle;

public CustomView(Context context, Activity act) {
    super(context);
    contextHandle = context;
    actHandle = act;
    //anotherB = new Button(contextHandle); //this shouldn't be necessary for 
    //instantiation from XML widget
    initCustomView();

}

public void initCustomView(){

     anotherB = (Button) findViewById(R.id.nextbutton);
     anotherB.setOnClickListener(this);

}


public Button getAnotherB(){
    return anotherB;
}

@Override
public void onFinishInflate(){
    anotherB.setOnClickListener(this); //it seems any addressing of child
    //views of the layout [the widgets] need to be made after the 
    //framework calls this method.
}

@Override
public void onClick(View arg0) {
    // TODO Auto-generated method stub

    Log.d("me", "Got the custom click!");
}




}

Теперь он правильно раскладывает макет и не бросает NPE. Конечно, обратный вызов onClickListener все еще не работает должным образом (сообщение «Получил пользовательский щелчок!» Никогда не появляется в logcat), но это другая проблема ...

спасибо всем CCJ

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...