Android can't find view by ID in fragment
-
16-06-2021 - |
Question
I have a custom EditText MomentumEditText
that I have in a layout in xml (fragment_edit_text.xml):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.frazerm.momentum.MomentumEditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/writing_edittext"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
I can successfully inflate and display the layout in a fragment, but trying to get the MomentumEditText
using View.findViewById(R.id.writing_edittext)
returns null.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_edit_text, container, true);
MomentumEditText editText = (MomentumEditText) root.findViewById(R.id.writing_edittext);
}
I have also tried to get the MomentumEditText from onStart()
, but it still returns null.
Edit = I tried replacing the custom EditText in the xml with a plain EditText, and findViewById() works! here is MomentumEditText:
package com.frazerm.momentum;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.widget.EditText;
public class MomentumEditText extends EditText {
boolean moveCursorDisabled = true;
boolean deleteCharsDisabled = true;
private static Paint linePaint;
private static Paint marginPaint;
MomentumEditText thisEditText = this;
Editable currentText;
public MomentumEditText(Context context, AttributeSet attrs) {
super(context);
setCursorVisible(true);
this.setGravity(Gravity.TOP);
TextWatcher inputTextWatcher = new TextWatcher() {
CharSequence textToAdd = "";
boolean charWasDeleted = false;
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (moveCursorDisabled) {
if( count > after ) {
textToAdd = s.subSequence(start + after, start + count);
charWasDeleted = true;
}
}
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (moveCursorDisabled) {
if (charWasDeleted == true) {
charWasDeleted = false;
s.append(textToAdd);
}
else {
thisEditText.setSelection( thisEditText.getText().length() );
}
}
}
};
this.addTextChangedListener(inputTextWatcher);
}
//disables ability to move the cursor to other parts of the text
@Override
public boolean onTouchEvent(MotionEvent event)
{
if (deleteCharsDisabled) {
final float eventX = event.getX();
if( getSelectionStart() != eventX )
{
super.onTouchEvent(event);
thisEditText.setSelection( thisEditText.getText().length() );
return true;
}
return super.onTouchEvent(event);
}
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
linePaint = new Paint();
linePaint.setColor(0x77777777);
//linePaint.setStyle(Style.STROKE);
marginPaint = new Paint();
marginPaint.setColor(0x99FF4444);
//marginPaint.setStyle(Style.STROKE);
Rect bounds = new Rect();
int firstLineY = getLineBounds(0, bounds);
int lineHeight = getLineHeight();
int totalLines = Math.max(getLineCount(), getHeight() / lineHeight);
for (int i = 0; i < totalLines; i++) {
int lineY = firstLineY + i * lineHeight + dip(2);
canvas.drawLine(0, lineY, bounds.right, lineY, linePaint);
}
canvas.drawLine( (float) dip(64), (float) 0, (float) dip(64), getHeight(), marginPaint );
super.onDraw(canvas);
setPadding(dip(64), 0, 0, 0);
}
public void setCursorMoveAllowed(boolean allowed) {
moveCursorDisabled = !allowed;
setCursorVisible(allowed);
return;
}
public void setBackspaceAllowed(boolean allowed) {
deleteCharsDisabled = !allowed;
return;
}
public boolean superTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
public int dip ( int dip ) {
float d = this.getResources().getDisplayMetrics().density;
int pixels = (int)(dip * d);
return pixels;
}
}
Solution
Because you are extending a current view you need to override all the constructors that can be used. Try:
public class MomentumEditText extends EditText {
public MomentumEditText(Context context) {
super(context);
init();
}
public MomentumEditText(Context context, AttributeSet attrs) {
super(context, attrs); // This is the constructor used by XML (you were missing the super call)
init();
}
public MomentumEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defstyle);
init();
}
private void init(){
setCursorVisible(true);
// your other code
}
}
Reference: http://developer.android.com/reference/android/widget/EditText.html (Public Constructors)
OTHER TIPS
Try setting attach to root false, since you are the root!
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_edit_text, container, false);
MomentumEditText editText = (MomentumEditText) root.findViewById(R.id.writing_edittext);
return root;
}
Try moving this line of code into your onActivityCreated
method instead:
MomentumEditText editText = (MomentumEditText) findViewById(R.id.writing_edittext);
I'm guessing that fragment_edit_text.xml
is the layout file you have shown in your question?
If yes, check if there is no other layout with the same name in some folders that specify some configurations (like language, density, etc).
For example you may have:
/layout/fragment_edit_text.xml
and
/layout-land/fragment_edit_text.xml
but only have your custom view within the layout-land
directory, therefore it will compile ok but when in portrait mode your findViewById
will return null