Android 自定义视图事件侦听器在没有静态引用的情况下无法正常工作
Android custom view event listeners not working properly without static reference
我正在 android 中创建一个自定义复合视图,它在相对布局中包含两个 TextView。有两个 class,一个是 BarcodeScannerView,它是复合视图,另一个 activity 使用自定义视图。代码如下
BarcodeScannerView.java
public class BarcodeScannerView extends RelativeLayout implements View.OnClickListener {
private int boxRadius;
private String boxText;
private int boxColor, boxEntityColor;
private TextView entityName;
private TextView manualEntry;
private ImageView scannerReadyImv;
private RelativeLayout scannerBox;
private OnClickListener listener;
private ScannerListener scanner;
private ScannerState state;
// private static BarcodeScannerView instance;
public enum ScannerState {
IDLE, READY, COMPLETE;
}
public interface ScannerListener {
void onClickScannerBox();
void scannerIdle();
}
public BarcodeScannerView(Context context) {
super(context);
}
public BarcodeScannerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
View.inflate(context, R.layout.activity_barcode_scanner, this);
setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
entityName = (TextView) findViewById(R.id.entityNameTV);
manualEntry = (TextView) findViewById(R.id.manualEntryTV);
scannerReadyImv = (ImageView) findViewById(R.id.scannerReadyImv);
scannerBox = (RelativeLayout) findViewById(R.id.scannerBox);
manualEntry.setOnClickListener(this);
scannerBox.setOnClickListener(this);
// Assign custom attributes
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.barcodeviewer, 0, 0);
try {
boxEntityColor = a.getInteger(R.styleable.barcodeviewer_boxEntityColor, 0);
setBoxEntityColor(boxEntityColor);
} finally {
a.recycle();
}
}
// Set custom attributes
private void setBoxEntityColor(int color) {
this.entityName.setTextColor(color);
}
// Set event handlers
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
public void setOnScannerListener(ScannerListener scanner) {
this.scanner = scanner;
}
// Setters and getters
public void setScannerState(ScannerState state) {
this.state = state;
}
public ScannerState getScannerState() {
return this.state;
}
private void setScannerReadyState() {
scannerBox.setBackgroundResource(R.drawable.bc_active_4);
scannerReadyImv.setVisibility(View.VISIBLE);
}
public void onClick(View v) {
if (v.getId() == R.id.scannerBox) {
this.scanner.onClickScannerBox();
if (this.getScannerState() == ScannerState.IDLE) {
setScannerReadyState();
}
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements BarcodeScannerView.ScannerListener {
private BarcodeScannerView barcodeScannerView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
/*barcodeScannerView = BarcodeScannerView.instance(MainActivity.this);
barcodeScannerView.setOnScannerListener(this);*/
barcodeScannerView = new BarcodeScannerView(this);
barcodeScannerView.setOnScannerListener(this);
}
@Override
public void onClickScannerBox() {
scannerIdle();
}
@Override
public void scannerIdle(){
barcodeScannerView.setScannerState(BarcodeScannerView.ScannerState.IDLE);
}
}
问题是当我调用 BarcodeScannerView 的 onClick() 方法时 class 它给出了一个空点异常。
java.lang.NullPointerException: Attempt to invoke interface method 'void [packagename]$ScannerListener.onClickScannerBox()' on a null object reference
在调试过程中,我注意到当调用 onClickScannerBox() 时,扫描仪对象被初始化。但是在 BarcodeScannerView 中执行 onClickScannerBox() 之后,扫描仪对象变为空。
根据我的观察,原因是当 onClickScannerBox() 被调用时,一个 BarcodeScannerView 对象实例被初始化,当 onClickScannerBox() 完成并退出该方法时,因为它在另一个 BarcodeScannerView 对象的 onClick() 方法中例如,扫描仪对象变为空。
我可以简单地使用静态扫描器对象更正此问题。 (这里没有提供代码,因为代码太长了)我不认为静态引用是解决这个问题的最佳方法。有没有什么方法可以在不使用任何静态引用的情况下更正问题?任何建议表示赞赏。
在您的实现中,您的自定义复合视图有两个实例发挥作用,一个在布局中(activity 布局),另一个在 activity onCreate
方法。
barcodeScannerView = new BarcodeScannerView(this);
barcodeScannerView.setOnScannerListener(this);
您已将侦听器设置为明确创建的侦听器,它不知道布局中使用的视图,只是它们是两个实例。您应该使用在 xml 中创建(膨胀)的实例,如下所示。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
barcodeScannerView = (BarcodeScannerView) findViewById(R.id.barcode_scanner);
barcodeScannerView.setOnScannerListener(this);
}
barcode_scanner
应该是您为 activity 布局中使用的复合视图提供的 ID。
我正在 android 中创建一个自定义复合视图,它在相对布局中包含两个 TextView。有两个 class,一个是 BarcodeScannerView,它是复合视图,另一个 activity 使用自定义视图。代码如下
BarcodeScannerView.java
public class BarcodeScannerView extends RelativeLayout implements View.OnClickListener {
private int boxRadius;
private String boxText;
private int boxColor, boxEntityColor;
private TextView entityName;
private TextView manualEntry;
private ImageView scannerReadyImv;
private RelativeLayout scannerBox;
private OnClickListener listener;
private ScannerListener scanner;
private ScannerState state;
// private static BarcodeScannerView instance;
public enum ScannerState {
IDLE, READY, COMPLETE;
}
public interface ScannerListener {
void onClickScannerBox();
void scannerIdle();
}
public BarcodeScannerView(Context context) {
super(context);
}
public BarcodeScannerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
View.inflate(context, R.layout.activity_barcode_scanner, this);
setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
entityName = (TextView) findViewById(R.id.entityNameTV);
manualEntry = (TextView) findViewById(R.id.manualEntryTV);
scannerReadyImv = (ImageView) findViewById(R.id.scannerReadyImv);
scannerBox = (RelativeLayout) findViewById(R.id.scannerBox);
manualEntry.setOnClickListener(this);
scannerBox.setOnClickListener(this);
// Assign custom attributes
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.barcodeviewer, 0, 0);
try {
boxEntityColor = a.getInteger(R.styleable.barcodeviewer_boxEntityColor, 0);
setBoxEntityColor(boxEntityColor);
} finally {
a.recycle();
}
}
// Set custom attributes
private void setBoxEntityColor(int color) {
this.entityName.setTextColor(color);
}
// Set event handlers
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
public void setOnScannerListener(ScannerListener scanner) {
this.scanner = scanner;
}
// Setters and getters
public void setScannerState(ScannerState state) {
this.state = state;
}
public ScannerState getScannerState() {
return this.state;
}
private void setScannerReadyState() {
scannerBox.setBackgroundResource(R.drawable.bc_active_4);
scannerReadyImv.setVisibility(View.VISIBLE);
}
public void onClick(View v) {
if (v.getId() == R.id.scannerBox) {
this.scanner.onClickScannerBox();
if (this.getScannerState() == ScannerState.IDLE) {
setScannerReadyState();
}
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements BarcodeScannerView.ScannerListener {
private BarcodeScannerView barcodeScannerView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
/*barcodeScannerView = BarcodeScannerView.instance(MainActivity.this);
barcodeScannerView.setOnScannerListener(this);*/
barcodeScannerView = new BarcodeScannerView(this);
barcodeScannerView.setOnScannerListener(this);
}
@Override
public void onClickScannerBox() {
scannerIdle();
}
@Override
public void scannerIdle(){
barcodeScannerView.setScannerState(BarcodeScannerView.ScannerState.IDLE);
}
}
问题是当我调用 BarcodeScannerView 的 onClick() 方法时 class 它给出了一个空点异常。
java.lang.NullPointerException: Attempt to invoke interface method 'void [packagename]$ScannerListener.onClickScannerBox()' on a null object reference
在调试过程中,我注意到当调用 onClickScannerBox() 时,扫描仪对象被初始化。但是在 BarcodeScannerView 中执行 onClickScannerBox() 之后,扫描仪对象变为空。
根据我的观察,原因是当 onClickScannerBox() 被调用时,一个 BarcodeScannerView 对象实例被初始化,当 onClickScannerBox() 完成并退出该方法时,因为它在另一个 BarcodeScannerView 对象的 onClick() 方法中例如,扫描仪对象变为空。
我可以简单地使用静态扫描器对象更正此问题。 (这里没有提供代码,因为代码太长了)我不认为静态引用是解决这个问题的最佳方法。有没有什么方法可以在不使用任何静态引用的情况下更正问题?任何建议表示赞赏。
在您的实现中,您的自定义复合视图有两个实例发挥作用,一个在布局中(activity 布局),另一个在 activity onCreate
方法。
barcodeScannerView = new BarcodeScannerView(this);
barcodeScannerView.setOnScannerListener(this);
您已将侦听器设置为明确创建的侦听器,它不知道布局中使用的视图,只是它们是两个实例。您应该使用在 xml 中创建(膨胀)的实例,如下所示。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
barcodeScannerView = (BarcodeScannerView) findViewById(R.id.barcode_scanner);
barcodeScannerView.setOnScannerListener(this);
}
barcode_scanner
应该是您为 activity 布局中使用的复合视图提供的 ID。