I want to achieve this layout. Table can have different number of rows, but once this number is set, it wouldn't change. Also, there is no need for scroll.
Well... I could do this with simple TableLayout, but it doesn't support data binding.
So I'm trying to implement custom AdapterView, but nothing is appeared in a screen.
public class MainActivity extends Activity {
String[][] data = new String[5][5];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout layout = (RelativeLayout) getLayoutInflater().inflate(R.layout.activity_main, null);
setContentView(layout);
SquareTableView sqt = new SquareTableView(this);
sqt.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
sqt.setNumColumns(5);
layout.addView(sqt);
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
data[i][j] = "a";
sqt.setAdapter(new CellArrayAdapter(this, data) {
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
public class SquareTableView extends AdapterView<ListAdapter> {
private ListAdapter adapter;
private int numColumns;
public int getNumColumns() {
return numColumns;
}
public void setNumColumns(int numColumns) {
this.numColumns = numColumns;
}
public SquareTableView(Context context) {
super(context);
}
#Override
public ListAdapter getAdapter() {
return adapter;
}
#Override
public View getSelectedView() {
// TODO Auto-generated method stub
return null;
}
#Override
public void setAdapter(ListAdapter adapter) {
this.adapter = adapter;
requestLayout();
}
#Override
public void setSelection(int position) {
// TODO Auto-generated method stub
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Context context = getContext();
int orientation = context.getResources().getConfiguration().orientation;
int sideSize;
if (orientation == Configuration.ORIENTATION_PORTRAIT)
sideSize = MeasureSpec.getSize(widthMeasureSpec);
else {
sideSize = MeasureSpec.getSize(heightMeasureSpec);
}
setMeasuredDimension(sideSize, sideSize);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
for (int i = 0; i < adapter.getCount(); i++) {
int childSide = getWidth() / numColumns;
int row = i / numColumns;
int column = i % numColumns;
int leftPosition = childSide * column;
int rightPosition = leftPosition + childSide;
int topPosition = childSide * row;
int bottomPosition = topPosition + childSide;
View child = adapter.getView(i, null, this);
child.layout(leftPosition, topPosition, rightPosition, bottomPosition);
child.forceLayout();
}
}
Related
I have RecyclerView item decoration and add my RecyclerView. RecyclerView divider lost when change item background color.
My RecyclerView with divider:
When RecyclerView items selected, not show divider:
setBackgroundColor:
private void setBackgroundColor() {
if (selectedItem.isEmpty() || !selectedItem.contains(item)) {
this.setBackgroundColor(Color.TRANSPARENT);
} else {
this.setBackgroundColor(0xffffedc7);
}
}
DividerItemDecoration:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
public DividerItemDecoration(int resId) {
mDivider = ContextCompat.getDrawable(MyApplication.getContext(), resId);
}
#Override
public void onDraw(#NonNull Canvas canvas, RecyclerView parent, #NonNull RecyclerView.State state) {
int left = MyApplication.getContext().getResources().getDimensionPixelSize(R.dimen.profileImageSizeDivider);
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
}
}
Try this:
/**
* Draws left or right inset dividers at the bottom of every recycler item view. Only supports
* vertical orientations.
*/
public class InsetDividerItemDecoration extends RecyclerView.ItemDecoration {
private int mLeftInset = 0;
private int mRightInset = 0;
private int mStartPosition = 0;
final private Paint mLinePaint = new Paint();
final private List<Integer> mPositionsToIgnore = new ArrayList<>();
public InsetDividerItemDecoration(int leftInset, int rightInset, int dividerColor) {
mLeftInset = leftInset;
mRightInset = rightInset;
setColor(dividerColor);
}
public InsetDividerItemDecoration(int leftInset, int rightInset, int dividerColor, int startPosition) {
mLeftInset = leftInset;
mRightInset = rightInset;
mStartPosition = startPosition;
setColor(dividerColor);
}
public InsetDividerItemDecoration(int leftInset, int rightInset, int dividerColor, int startPosition, int dividerHeight) {
mLeftInset = leftInset;
mRightInset = rightInset;
mStartPosition = startPosition;
setColor(dividerColor);
setDividerHeight(dividerHeight);
}
public InsetDividerItemDecoration(int leftInset, int rightInset) {
this(leftInset, rightInset, 0);
}
public int getColor() {
return mLinePaint.getColor();
}
public void setColor(int color) {
mLinePaint.setColor(color);
}
public int getLeftInset() {
return mLeftInset;
}
public void setLeftInset(int leftInset) {
mLeftInset = leftInset;
}
public int getRightInset() {
return mRightInset;
}
public void setRightInset(int rightInset) {
mRightInset = rightInset;
}
public void setDividerHeight(int height) {
mLinePaint.setStrokeWidth(height);
}
public int getDividerHeight() {
return (int) mLinePaint.getStrokeWidth();
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
final int visibleItems = parent.getLayoutManager().getChildCount();
for (int i = 0; i < visibleItems; i++) {
View itemView = parent.getLayoutManager().getChildAt(i);
final int adapterPosition = parent.getChildAdapterPosition(itemView);
final boolean shouldDraw = adapterPosition != RecyclerView.NO_POSITION
&& adapterPosition != parent.getAdapter().getItemCount() - 1
&& adapterPosition >= mStartPosition
&& !shouldIgnoreAdapterPosition(adapterPosition);
if (shouldDraw) {
Rect itemRect = new Rect();
itemView.getDrawingRect(itemRect);
parent.offsetDescendantRectToMyCoords(itemView, itemRect);
final int lineStartX = mLeftInset;
final int lineStartY = itemRect.bottom;
final int lineEndX = itemRect.right - mRightInset;
final int lineEndY = lineStartY;
c.drawLine(lineStartX, lineStartY, lineEndX, lineEndY, mLinePaint);
}
}
}
protected boolean shouldIgnoreAdapterPosition(int position) {
return mPositionsToIgnore.size() > 0 && mPositionsToIgnore.contains(position);
}
public void setStartPosition(int startPosition) {
mStartPosition = startPosition;
}
public void setPositionsToIgnore(List<Integer> positionsToIgnore) {
mPositionsToIgnore.clear();
if (positionsToIgnore != null) {
mPositionsToIgnore.addAll(positionsToIgnore);
}
}
public void setPositionsToIgnore(Integer[] positionsToIgnore) {
setPositionsToIgnore(Arrays.asList(positionsToIgnore));
}
}
I have a custom view PasscodeView in my dialog fragment layout.
PasscodeView.java:
public class PasscodeView extends ViewGroup {
EditText mEditText;
int mDigitCount;
private int mDigitWidth;
private int mDigitRadius;
private int mOuterStrokeWidth;
private int mInnerStrokeWidth;
private int mDigitInnerRadius;
private int mDigitSpacing;
private int mDigitElevation;
private int mControlColor;
private int mHighlightedColor;
private int mInnerColor;
private int mInnerBorderColor;
private OnFocusChangeListener mOnFocusChangeListener;
private PasscodeEntryListener mPasscodeEntryListener;
public PasscodeView(Context context) {
this(context, null);
}
public PasscodeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PasscodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Get style information
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.PasscodeView);
mDigitCount = array.getInt(R.styleable.PasscodeView_numDigits, 4);
// Dimensions
DisplayMetrics metrics = getResources().getDisplayMetrics();
mDigitRadius = array.getDimensionPixelSize(R.styleable.PasscodeView_digitRadius,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, metrics));
mOuterStrokeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, metrics);
mInnerStrokeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, metrics);
mDigitInnerRadius = mDigitRadius - ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, metrics));
mDigitWidth = (mDigitRadius + mOuterStrokeWidth) * 2;
mDigitSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, metrics);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mDigitElevation = array.getDimensionPixelSize(R.styleable.PasscodeView_digitElevation, 0);
}
// Get theme to resolve defaults
Resources.Theme theme = getContext().getTheme();
mControlColor = Color.DKGRAY;
// Text colour, default to android:colorControlNormal from theme
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue controlColor = new TypedValue();
theme.resolveAttribute(android.R.attr.colorControlNormal, controlColor, true);
mControlColor = controlColor.resourceId > 0 ? getResources().getColor(controlColor.resourceId) :
controlColor.data;
}
mControlColor = array.getColor(R.styleable.PasscodeView_controlColor, mControlColor);
// Accent colour, default to android:colorAccent from theme
mHighlightedColor = Color.LTGRAY;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue accentColor = new TypedValue();
theme.resolveAttribute(R.attr.colorControlHighlight, accentColor, true);
mHighlightedColor = accentColor.resourceId > 0 ? getResources().getColor(accentColor.resourceId) :
accentColor.data;
}
mHighlightedColor = array.getColor(R.styleable.PasscodeView_controlColorActivated, mHighlightedColor);
//color for the inner circle
mInnerColor = Color.CYAN;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue innerColor = new TypedValue();
theme.resolveAttribute(android.R.attr.colorPrimary, innerColor, true);
mInnerColor = innerColor.resourceId > 0 ? getResources().getColor(innerColor.resourceId) :
innerColor.data;
}
mInnerColor = array.getColor(R.styleable.PasscodeView_digitColorFilled, mInnerColor);
//color for the inner circle border
mInnerBorderColor = Color.GREEN;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TypedValue innerBorderColor = new TypedValue();
theme.resolveAttribute(android.R.attr.colorPrimaryDark, innerBorderColor, true);
mInnerBorderColor = innerBorderColor.resourceId > 0 ? getResources().getColor(innerBorderColor.resourceId) :
innerBorderColor.data;
}
mInnerBorderColor = array.getColor(R.styleable.PasscodeView_digitColorBorder, mInnerBorderColor);
// Recycle the typed array
array.recycle();
// Add child views
setupViews();
}
#Override
public boolean shouldDelayChildPressedState() {
return false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Measure children
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
// Calculate the size of the view
int width = (mDigitWidth * mDigitCount) + (mDigitSpacing * (mDigitCount - 1));
setMeasuredDimension(
width + getPaddingLeft() + getPaddingRight() + (mDigitElevation * 2),
mDigitWidth + getPaddingTop() + getPaddingBottom() + (mDigitElevation * 2));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Position the child views
for (int i = 0; i < mDigitCount; i++) {
View child = getChildAt(i);
int left = i * mDigitWidth + (i > 0 ? i * mDigitSpacing : 0);
child.layout(
left + getPaddingLeft() + mDigitElevation,
getPaddingTop() + (mDigitElevation / 2),
left + getPaddingLeft() + mDigitElevation + mDigitWidth,
getPaddingTop() + (mDigitElevation / 2) + mDigitWidth);
}
// Add the edit text as a 1px wide view to allow it to focus
getChildAt(mDigitCount).layout(0, 0, 1, getMeasuredHeight());
}
private void setupViews() {
setWillNotDraw(false);
// Add a digit view for each digit
for (int i = 0; i < mDigitCount; i++) {
DigitView digitView = new DigitView(getContext(), i);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
digitView.setElevation(mDigitElevation);
}
addView(digitView);
}
// Add an "invisible" edit text to handle input
mEditText = new EditText(getContext());
mEditText.setBackgroundColor(getResources().getColor(android.R.color.transparent));
mEditText.setTextColor(getResources().getColor(android.R.color.transparent));
mEditText.setCursorVisible(false);
mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mDigitCount)});
mEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
mEditText.setKeyListener(DigitsKeyListener.getInstance("1234567890"));
mEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
// Update the selected state of the views
int length = mEditText.getText().length();
updateChilViewSelectionStates(length, hasFocus);
// Make sure the cursor is at the end
mEditText.setSelection(length);
// Provide focus change events to any listener
if (mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChange(PasscodeView.this, hasFocus);
}
}
});
mEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
int length = s.length();
updateChilViewSelectionStates(length, mEditText.hasFocus());
if (length == mDigitCount && mPasscodeEntryListener != null) {
mPasscodeEntryListener.onPasscodeEntered(s.toString());
}
}
});
addView(mEditText);
invalidate();
}
private void updateChilViewSelectionStates(int length, boolean hasFocus) {
for (int i = 0; i < mDigitCount; i++) {
getChildAt(i).setSelected(hasFocus && i == length);
}
}
/**
* Get the {#link Editable} from the EditText
*
* #return
*/
public Editable getText() {
return mEditText.getText();
}
/**
* Set text to the EditText
*
* #param text
*/
public void setText(CharSequence text) {
if (text.length() > mDigitCount) {
text = text.subSequence(0, mDigitCount);
}
mEditText.setText(text);
invalidateChildViews();
}
/**
* Clear passcode input
*/
public void clearText() {
mEditText.setText("");
invalidateChildViews();
}
private void invalidateChildViews() {
for (int i = 0; i < mDigitCount; i++) {
getChildAt(i).invalidate();
}
}
public void setPasscodeEntryListener(PasscodeEntryListener mPasscodeEntryListener) {
this.mPasscodeEntryListener = mPasscodeEntryListener;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
requestToShowKeyboard();
return true;
}
return super.onTouchEvent(event);
}
/**
* Requests the view to be focused and the keyboard to be popped-up
*/
public void requestToShowKeyboard() {
// Make sure this view is focused
mEditText.requestFocus();
// Show keyboard
InputMethodManager inputMethodManager = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(mEditText, 0);
}
#Override
public OnFocusChangeListener getOnFocusChangeListener() {
return mOnFocusChangeListener;
}
#Override
public void setOnFocusChangeListener(OnFocusChangeListener l) {
mOnFocusChangeListener = l;
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable parcelable = super.onSaveInstanceState();
SavedState savedState = new SavedState(parcelable);
savedState.editTextValue = mEditText.getText().toString();
return savedState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
mEditText.setText(savedState.editTextValue);
mEditText.setSelection(savedState.editTextValue.length());
}
/**
* Listener that gets notified when the complete passcode has been entered
*/
public interface PasscodeEntryListener {
/**
* Called when all the digits of the passcode has been entered
*
* #param passcode - The entered passcode
*/
void onPasscodeEntered(String passcode);
}
static class SavedState extends BaseSavedState {
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
#Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
String editTextValue;
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel source) {
super(source);
editTextValue = source.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(editTextValue);
}
}
class DigitView extends View {
private Paint mOuterPaint, mInnerPaint;
private int mPosition = 0;
public DigitView(Context context, int position) {
this(context);
mPosition = position;
}
public DigitView(Context context) {
this(context, null);
}
public DigitView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DigitView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init() {
setWillNotDraw(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
mOuterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOuterPaint.setAlpha(255);
mOuterPaint.setDither(true);
mOuterPaint.setStyle(Paint.Style.STROKE);
mOuterPaint.setStrokeWidth(mOuterStrokeWidth);
mOuterPaint.setStrokeCap(Paint.Cap.ROUND);
mOuterPaint.setStrokeJoin(Paint.Join.ROUND);
mOuterPaint.setShadowLayer(2, 0, 0, Color.parseColor("#B4999999"));
mInnerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerPaint.setAlpha(255);
mInnerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mInnerPaint.setStrokeWidth(mInnerStrokeWidth);
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint.setStrokeJoin(Paint.Join.ROUND);
mInnerPaint.setColor(mInnerColor);
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mDigitWidth, mDigitWidth);
}
#Override
protected void onDraw(Canvas canvas) {
float center = getWidth() / 2;
if (isSelected()) {
mOuterPaint.setColor(mHighlightedColor);
} else {
mOuterPaint.setColor(mControlColor);
}
canvas.drawColor(Color.TRANSPARENT);
canvas.drawCircle(center, center, mDigitRadius, mOuterPaint);
if (mEditText.getText().length() > mPosition) {
canvas.drawCircle(center, center, mDigitInnerRadius, mInnerPaint);
}
}
}
Now, I want to open number keyboard as soon as dialog shows up. For this, I have a method requestToShowKeyboard() in PasscodeView.java to open keyboard.
But, keyboard doesn't open when dialog shows up. Below is the code I wrote inside onViewCreated().
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mPasscodeView.requestToShowKeyboard();
}
This is how I used PasscodeView in dialog fragment layout:
<com.via.android.customview.PasscodeView
android:id="#+id/edtOptCode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="#dimen/dimen_10_dp"
android:background="#null"
android:gravity="center"
android:padding="#dimen/dimen_8_dp"
passcodeView:digitColorBorder="#FFFFFF"
passcodeView:digitColorFilled="#9f9f9f"
passcodeView:digitRadius="12dp"
passcodeView:numDigits="6" />
If anyone can help me with this.
Solved: Solution to my problem has been turned out to this as below:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mPasscodeView.post(new Runnable() {
#Override
public void run() {
mPasscodeView.requestToShowKeyboard();
}
});
}
Instead of calling in onViewCreated(), try calling like
mPasscodeView.post(new Runnable() {
#Override
public void run() {
mPasscodeView.requestToShowKeyboard();
}
});
So this will add the runnable to the queue to execute in next pass.
I tried to create Grid Layout for this I used below code. But in that code I don't know what is ItemView and how to implement it. When I put this code in my project I get error can not resolve symbol "ItemView". I got this code from below link -
https://stackoverflow.com/a/36143314/5726392
public class PrivateSeatViews extends FrameLayout implements View.OnClickListener {
private GridLayout mGridView;
private int mRowsCount;
private int mColsCount;
private int mCellSpace;
private OnItemClickListener mOnItemClickListener;
public PrivateSeatViews(Context context) {
super(context);
init(context, null);
}
// other constructors
private void init(Context context, AttributeSet attrs) {
// default values
View layout = inflate(getContext(), R.layout.grid_layout, null);
mGridView = (GridLayout) layout.findViewById(R.id.Lower);
mGridView.setRowCount(mRowsCount);
mGridView.setColumnCount(mColsCount);
mGridView.post(new Runnable() {
#Override
public void run() {
int width = getMeasuredWidth() / getColumnsCount();
int height = getMeasuredHeight() / getRowsCount();
for (int i = 0; i < getRowsCount(); i++) {
for (int j = 0; j < getColumnsCount(); j++) {
GridLayout.LayoutParams params = (GridLayout.LayoutParams) getChildAt(i, j).getL;
params.width = width;
params.height = height;
getChildAt(i, j).setLayoutParams(params);
}
}
}
});
addView(layout);
}
// this method allows to dinamically create grid
public void buildChildren(int rowsCount, int colsCount) {
mRowsCount = rowsCount;
mColsCount = colsCount;
mGridView.setRowCount(mRowsCount);
mGridView.setColumnCount(mColsCount);
buildChildren();
}
public void buildChildren() {
for (int i = 0; i < getRowsCount(); i++) {
for (int j = 0; j < getColumnsCount(); j++) {
ItemView1 view = new ItemView1(getContext(), i, j);
view.setOnClickListener(this);
mGridView.addView(view);
}
}
}
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
public ItemView getChildAt(int rowIndex, int columnIndex) {
int index = (getColumnsCount() * rowIndex) + columnIndex;
return (ItemView) mGridView.getChildAt(index);
}
public boolean isTouchOn(int rowIndex, int columnIndex) {
return getChildAt(rowIndex, columnIndex).isTouchOn();
}
public int getColumnsCount() {
return mGridView.getColumnCount();
}
public int getRowsCount() {
return mGridView.getRowCount();
}
#Override
public void onClick(View v) {
if (v instanceof ItemView) {
ItemView view = (ItemView) v;
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(view);
}
}
}
public interface OnItemClickListener {
void onItemClick(ItemView view);
}
}
The best way i found to create the GridView is to use RecyclerView with the GridLayouy manager. Adapter implementations is the same as for normal RecyclerView
You can create GridLayout manager like this
//3 is the row span
GridLayoutManager a = new GridLayoutManager(this, 3);
This will help you to improve performance and create your view with the ViewHolder pattern
If you never used RecyclerView you can see a fine example here
I just followed this tutorial, to create a custom View as an item of a GridLayout.
That's my CustomView
public class RowView extends View{
boolean touchOn;
boolean mDownTouch = false;
private OnToggledListener toggledListener;
int _IdRow = 0;
int _IdColumn = 0;
public RowView(Context context, int Rows, int Columns) {
super(context);
this._IdRow = Rows;
this._IdColumn = Columns;
init();
}
public RowView(Context context) {
super(context);
init();
}
public RowView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RowView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
touchOn = false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
#Override
protected void onDraw(Canvas canvas) {
if (touchOn) {
canvas.drawColor(Color.RED);
} else {
canvas.drawColor(Color.GRAY);
}
}
//onClick not possible to use on custom View so, onTouchEvent is the solution
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
//if Click
case MotionEvent.ACTION_DOWN:
touchOn = !touchOn;
invalidate();
if(toggledListener != null){
toggledListener.OnToggled(this, touchOn);
}
mDownTouch = true;
return true;
case MotionEvent.ACTION_UP:
if (mDownTouch) {
mDownTouch = false;
performClick();
return true;
}
}
return false;
}
#Override
public boolean performClick() {
super.performClick();
return true;
}
public void setOnToggledListener(OnToggledListener listener){
toggledListener = listener;
}
public int get_IdRow() {
return _IdRow;
}
public int get_IdColumn() {
return _IdColumn;
}
On this class I can detect when user clicks on an item of GridLayout and change it to another color, that's ok.
But the problem comes at the time to create this :
This is my MainActivity where I show the GridLayout :
int numOfCol = mGridLayout.getColumnCount();
int numOfRow = mGridLayout.getRowCount();
mRowViews = new RowView[numOfCol*numOfRow];
for(int yPos=0; yPos<numOfRow; yPos++){
for(int xPos=0; xPos<numOfCol; xPos++){
RowView tView = new RowView(this, xPos, yPos);
tView.setOnToggledListener(this);
mRowViews[yPos*numOfCol + xPos] = tView;
mGridLayout.addView(tView);
}
}
mGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
#Override
public void onGlobalLayout() {
final int MARGIN = 5;
int pWidth = mGridLayout.getWidth();
int pHeight = mGridLayout.getHeight();
int numOfCol = mGridLayout.getColumnCount();
int numOfRow = mGridLayout.getRowCount();
int w = pWidth/numOfCol;
int h = pHeight/numOfRow;
for(int yPos=0; yPos<numOfRow; yPos++){
for(int xPos=0; xPos<numOfCol; xPos++){
GridLayout.LayoutParams params =
(GridLayout.LayoutParams)mRowViews[yPos*numOfCol + xPos].getLayoutParams();
params.width = w - 2*MARGIN;
params.height = h - 2*MARGIN;
params.setMargins(MARGIN, MARGIN, MARGIN, MARGIN);
mRowViews[yPos*numOfCol + xPos].setLayoutParams(params);
}
}
}});
Also there is a method of the Interface OnToggledListener that gives to me the row and column of my GridLayout when an item of it is clicked :
#Override
public void OnToggled(MyView v, boolean touchOn) {
//get the id string
String idString = v.get_IdRow() + ":" + v.get_IdColumn();
}
I'd like to avoid to create that mGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() because it fills on the screen thing that I don't want... I tried to put GridLayout 6x6 with android:layout_height="400dp" and it only show 3x3 and this is the LogCat message
D/android.widget.GridLayout: vertical constraints: y6-y0>=1749, y6-y5<=291, y5-y4<=291, y4-y3<=291, y3-y2<=291, y2-y1<=291, y1-y0<=291 are inconsistent; permanently removing: y6-y5<=291.
I'd like to do something like GridLayout[row][colum] to get the color of background and then do stuff, but I'm not able to find this solution.
For simplifying, you can implement a custom Board view wrapping the GridLayout and related logic. Below I report a possible approach.
Expectation here is to have an ItemView for representing one single cell in the board.
public class Board extends FrameLayout implements View.OnClickListener {
private GridLayout mGridView;
private int mRowsCount;
private int mColsCount;
private int mCellSpace;
private OnItemClickListener mOnItemClickListener;
public Board(Context context) {
super(context);
init(context, null);
}
// other constructors
private void init(Context context, AttributeSet attrs) {
// default values
mRowsCount = 1;
mColsCount = 1;
View layout = inflate(getContext(), R.layout.view_lights_board, null);
mGridView = (GridLayout) layout.findViewById(R.id.view_grid);
mGridView.setRowCount(mRowsCount);
mGridView.setColumnCount(mColsCount);
mGridView.post(new Runnable() {
#Override
public void run() {
int width = getMeasuredWidth() / getColumnsCount();
int height = getMeasuredHeight() / getRowsCount();
for (int i = 0; i < getRowsCount(); i++) {
for (int j = 0; j < getColumnsCount(); j++) {
GridLayout.LayoutParams params = (GridLayout.LayoutParams)
getChildAt(i, j).getLayoutParams();
params.width = width;
params.height = height;
getChildAt(i, j).setLayoutParams(params);
}
}
}
});
addView(layout);
}
// this method allows to dinamically create grid
public void buildChildren(int rowsCount, int colsCount) {
mRowsCount = rowsCount;
mColsCount = colsCount;
mGridView.setRowCount(mRowsCount);
mGridView.setColumnCount(mColsCount);
buildChildren();
}
public void buildChildren() {
for (int i = 0; i < getRowsCount(); i++) {
for (int j = 0; j < getColumnsCount(); j++) {
ItemView view = new ItemView(getContext(), i, j);
view.setOnClickListener(this);
mGridView.addView(view);
}
}
}
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
public ItemView getChildAt(int rowIndex, int columnIndex) {
int index = (getColumnsCount() * rowIndex) + columnIndex;
return (ItemView) mGridView.getChildAt(index);
}
public boolean isTouchOn(int rowIndex, int columnIndex) {
return getChildAt(rowIndex, columnIndex).isTouchOn();
}
public int getColumnsCount() {
return mGridView.getColumnCount();
}
public int getRowsCount() {
return mGridView.getRowCount();
}
#Override
public void onClick(View v) {
if (v instanceof ItemView) {
ItemView view = (ItemView) v;
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(view);
}
}
}
public interface OnItemClickListener {
void onItemClick(ItemView view);
}
}
In your Activity layout you will have something like this (here I assume your app package is com.android.example):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.android.example.Board
android:id="#+id/grid"
android:layout_width="match_parent"
android:layout_height="400dp" />
</FrameLayout>
And this is possible implementation of the Activity:
public class MainActivity extends AppCompatActivity implements LightsOutBoard.OnItemClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Board board = (Board) findViewById(R.id.grid);
board.setOnItemClickListener(this);
board.buildChildren(3, 3);
}
#Override
public void onItemClick(ItemView view) {
String text = view.getRowIndex() + " - " + view.getColumnIndex();
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
}
Hope this could help.
i have created an app in which there are 4 tabs in the action bar .
i have used a manuelpeinado.fadingactionbar .
while scrolling ,it works for the first tab but when i shift to second tab color is not changing .
i looked in to logcat and find that values of "mActionBarBackgroundDrawable.setAlpha(newAlpha);"
is changing but color is not changing.
its my 1st question on stackoverflow if i missed something then sorry for that .
thanks in ADV.
public class FadingActionBarHelper {
protected static final String TAG = "FadingActionBarHelper";
private Drawable mActionBarBackgroundDrawable;
private FrameLayout mHeaderContainer;
private int mActionBarBackgroundResId;
private int mHeaderLayoutResId;
private View mHeaderView;
private int mContentLayoutResId;
private View mContentView;
private ActionBar mActionBar;
private LayoutInflater mInflater;
private boolean mLightActionBar;
private boolean mUseParallax = true;
private int mLastDampedScroll;
private int mLastHeaderHeight = -1;
private ViewGroup mContentContainer;
private ViewGroup mScrollView;
private boolean mFirstGlobalLayoutPerformed;
private View mMarginView;
private View mListViewBackgroundView;
private double speed = 0;
public FadingActionBarHelper actionBarBackground(int drawableResId) {
mActionBarBackgroundResId = drawableResId;
return this;
}
public FadingActionBarHelper actionBarBackground(Drawable drawable) {
mActionBarBackgroundDrawable = drawable;
return this;
}
public FadingActionBarHelper headerLayout(int layoutResId) {
mHeaderLayoutResId = layoutResId;
return this;
}
public FadingActionBarHelper headerView(View view) {
mHeaderView = view;
return this;
}
public FadingActionBarHelper contentLayout(int layoutResId) {
mContentLayoutResId = layoutResId;
return this;
}
public FadingActionBarHelper contentView(View view) {
mContentView = view;
return this;
}
public FadingActionBarHelper lightActionBar(boolean value) {
mLightActionBar = value;
return this;
}
public FadingActionBarHelper parallax(boolean value) {
mUseParallax = value;
return this;
}
public double getSpeed(){
return speed;
}
public View createView(Context context) {
return createView(LayoutInflater.from(context));
}
public View createView(LayoutInflater inflater) {
//
// Prepare everything
mInflater = inflater;
if (mContentView == null) {
mContentView = inflater.inflate(mContentLayoutResId, null);
}
if (mHeaderView == null) {
mHeaderView = inflater.inflate(mHeaderLayoutResId, mHeaderContainer, false);
}
//
// See if we are in a ListView or ScrollView scenario
ListView listView = (ListView) mContentView.findViewById(android.R.id.list);
View root;
if (listView != null) {
root = createListView(listView);
} else {
root = createScrollView();
}
// Use measured height here as an estimate of the header height, later on after the layout is complete
// we'll use the actual height
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY);
mHeaderView.measure(widthMeasureSpec, heightMeasureSpec);
updateHeaderHeight(mHeaderView.getMeasuredHeight());
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int headerHeight = mHeaderContainer.getHeight();
if (!mFirstGlobalLayoutPerformed && headerHeight != 0) {
updateHeaderHeight(headerHeight);
mFirstGlobalLayoutPerformed = true;
}
}
});
return root;
}
public void initActionBar(Activity activity) {
mActionBar = getActionBar(activity);
if (mActionBarBackgroundDrawable == null) {
mActionBarBackgroundDrawable = activity.getResources().getDrawable(mActionBarBackgroundResId);
}
mActionBar.setBackgroundDrawable(mActionBarBackgroundDrawable);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
mActionBarBackgroundDrawable.setCallback(mDrawableCallback);
Toast.makeText(activity, "Inside initActionBar version less than jellybean", Toast.LENGTH_SHORT);
}
Toast.makeText(activity, " Inside initActionBar ", Toast.LENGTH_SHORT);
mActionBarBackgroundDrawable.setAlpha(0);
}
protected ActionBar getActionBar(Activity activity) {
return activity.getActionBar();
}
private Drawable.Callback mDrawableCallback = new Drawable.Callback() {
#Override
public void invalidateDrawable(Drawable who) {
mActionBar.setBackgroundDrawable(who);
}
#Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
}
#Override
public void unscheduleDrawable(Drawable who, Runnable what) {
}
};
private View createScrollView() {
mScrollView = (ViewGroup) mInflater.inflate(R.layout.fab__scrollview_container, null);
NotifyingScrollView scrollView = (NotifyingScrollView) mScrollView.findViewById(R.id.fab__scroll_view);
scrollView.setOnScrollChangedListener(mOnScrollChangedListener);
mContentContainer = (ViewGroup) mScrollView.findViewById(R.id.fab__container);
mContentContainer.addView(mContentView);
mHeaderContainer = (FrameLayout) mScrollView.findViewById(R.id.fab__header_container);
initializeGradient(mHeaderContainer);
mHeaderContainer.addView(mHeaderView, 0);
mMarginView = mContentContainer.findViewById(R.id.fab__content_top_margin);
return mScrollView;
}
private NotifyingScrollView.OnScrollChangedListener mOnScrollChangedListener = new NotifyingScrollView.OnScrollChangedListener() {
public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
onNewScroll(t);
}
};
private View createListView(ListView listView) {
mContentContainer = (ViewGroup) mInflater.inflate(R.layout.fab__listview_container, null);
mContentContainer.addView(mContentView);
mHeaderContainer = (FrameLayout) mContentContainer.findViewById(R.id.fab__header_container);
initializeGradient(mHeaderContainer);
mHeaderContainer.addView(mHeaderView, 0);
mMarginView = new View(listView.getContext());
mMarginView.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, 0));
listView.addHeaderView(mMarginView, null, false);
// Make the background as high as the screen so that it fills regardless of the amount of scroll.
mListViewBackgroundView = mContentContainer.findViewById(R.id.fab__listview_background);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mListViewBackgroundView.getLayoutParams();
params.height = Utils.getDisplayHeight(listView.getContext());
mListViewBackgroundView.setLayoutParams(params);
listView.setOnScrollListener(new CustomScrollListener());
return mContentContainer;
}
private class CustomScrollListener implements OnScrollListener{
private int previousFirstVisibleItem = 0;
private long previousEventTime = 0, currTime, timeToScrollOneElement;
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View topChild = null;
topChild = view.getChildAt(0);
if (topChild == null) {
onNewScroll(0);
} else if (topChild != mMarginView) {
onNewScroll(mHeaderContainer.getHeight());
} else {
onNewScroll(-topChild.getTop());
}
if (previousFirstVisibleItem != firstVisibleItem) {
currTime = System.currentTimeMillis();
timeToScrollOneElement = currTime - previousEventTime;
speed = ((double) 1 / timeToScrollOneElement) * 1000;
previousFirstVisibleItem = firstVisibleItem;
previousEventTime = currTime;
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
}
private int mLastScrollPosition ;
private void onNewScroll(int scrollPosition) {
if (mActionBar == null) {
return;
}
int currentHeaderHeight = mHeaderContainer.getHeight();
if (currentHeaderHeight != mLastHeaderHeight) {
updateHeaderHeight(currentHeaderHeight); // to make position of header at its default pos.
}
int headerHeight = currentHeaderHeight - mActionBar.getHeight();
float ratio = (float) Math.min(Math.max(scrollPosition, 0), headerHeight) / headerHeight;
int newAlpha = (int) (ratio * 255);
Log.d(" Inside onNewScroll() ", ""+newAlpha);
mActionBarBackgroundDrawable.setAlpha(newAlpha);
// addParallaxEffect(scrollPosition);
}
private void addParallaxEffect(int scrollPosition) {
float damping = mUseParallax ? 0.5f : 1.0f;
int dampedScroll = (int) (scrollPosition * damping);
int offset = mLastDampedScroll - dampedScroll;
Log.d(" Inside addParallaxEffect() ", ""+offset);
mHeaderContainer.offsetTopAndBottom(offset);
if (mListViewBackgroundView != null) {
offset = mLastScrollPosition - scrollPosition;
mListViewBackgroundView.offsetTopAndBottom(offset);
}
if (mFirstGlobalLayoutPerformed) {
mLastScrollPosition = scrollPosition;
mLastDampedScroll = dampedScroll;
}
}
private void updateHeaderHeight(int headerHeight) {
ViewGroup.LayoutParams params = (ViewGroup.LayoutParams) mMarginView.getLayoutParams();
params.height = headerHeight;
mMarginView.setLayoutParams(params);
if (mListViewBackgroundView != null) {
FrameLayout.LayoutParams params2 = (FrameLayout.LayoutParams) mListViewBackgroundView.getLayoutParams();
params2.topMargin = headerHeight;
mListViewBackgroundView.setLayoutParams(params2);
}
mLastHeaderHeight = headerHeight;
}
private void initializeGradient(ViewGroup headerContainer) {
View gradientView = headerContainer.findViewById(R.id.fab__gradient);
int gradient = R.drawable.fab__gradient;
if (mLightActionBar) {
gradient = R.drawable.fab__gradient_light;
}
gradientView.setBackgroundResource(gradient);
}
}