StaggeredGridLayoutManager make auto_fit columns - android

How i can make StaggeredGridLayoutManager auto_fit with all screens size , set number of columns automatically
recyclerViewadapter = new RecyclerViewAdapter(GetDataAdapter1, this);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(recyclerViewadapter);

If your items are at the same width, you can do this:
mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mRecyclerView.getViewTreeObserver().removeOnGLobalLayoutListener(this);
int viewWidth = mRecyclerView.getMeasuredWidth();
float cardViewWidth = getActivity().getResources().getDimension(R.dimen.cardview_layout_width);
int newSpanCount = (int) Math.floor(viewWidth / cardViewWidth);
mLayoutManager.setSpanCount(newSpanCount);
mLayoutManager.requestLayout();
}
});

Create help class for getting sizes.
public class GetSize {
public static int getColumnCount(Context context, int dp) {
if ((double) (getScreenWidth(context)/getPointOfDp(context, dp)) <= 1) return 1;
else return getScreenWidth(context)/getPointOfDp(context, dp);
}
public static int getScreenWidth(Context context){
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
return point.x; //Screen width
}
private static int getPointOfDp(Context context, int dp) { //Convert from dp to screen dots
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
}
For get a column count need set some minimal width of your item.
int columnCount = GetSize.getColumnCount(context, 288);
layoutManager = new StaggeredGridLayoutManager(columnCount, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);

Try this
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(getSpan(), StaggeredGridLayoutManager.VERTICAL);
public int getSpan() {
int orientation = getScreenOrientation(getActivity());
if (isTV(getActivity())) return 4;
if (isTablet(getActivity()))
return orientation == Configuration.ORIENTATION_PORTRAIT ? 2 : 3;
return orientation == Configuration.ORIENTATION_PORTRAIT ? 2 : 3;
}
public static boolean isTV(Context context) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2 && ((UiModeManager) context
.getSystemService(Context.UI_MODE_SERVICE))
.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
}
public static int getScreenOrientation(Context context) {
return context.getResources().getDisplayMetrics().widthPixels <
context.getResources().getDisplayMetrics().heightPixels ?
Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
}
public static boolean isTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}

Related

How to design spannable gridview using recyclerview (SpannableGridLayoutManager)

I want to design grid view like below image provided. The first item should be bigger than the rest.
Currently I am using RelativeLayout with GridLayoutManager check below code
RecyclerView recyclerView = (RecyclerView)
findViewById(R.id.recycler_view1);
RecyclerView.LayoutManager recyclerViewLayoutManager = new
GridLayoutManager(context, 3);
recyclerView.setLayoutManager(recyclerViewLayoutManager);
recyclerView_Adapter = new RecyclerViewAdapter(context,numbers);
recyclerView.setAdapter(recyclerView_Adapter);
Dummy array for demo
String[] numbers = {
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
};
Adapter Class
public class RecyclerViewAdapter extends
RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{
String[] values;
Context context1;
public RecyclerViewAdapter(Context context2,String[] values2){
values = values2;
context1 = context2;
}
public static class ViewHolder extends RecyclerView.ViewHolder{
public TextView textView;
public ViewHolder(View v){
super(v);
textView = (TextView) v.findViewById(R.id.textview1);
}
}
#Override
public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view1 = LayoutInflater.from(context1).inflate(R.layout.recycler_view_items,parent,false);
ViewHolder viewHolder1 = new ViewHolder(view1);
return viewHolder1;
}
#Override
public void onBindViewHolder(ViewHolder Vholder, int position){
Vholder.textView.setText(values[position]);
Vholder.textView.setBackgroundColor(Color.CYAN);
Vholder.textView.setTextColor(Color.BLUE);
}
#Override
public int getItemCount(){
return values.length;
} }
I have implemented the SpannableGridLayoutManager for this and its working perfectly for me. Please check the following solution.
Activity Code
SpannableGridLayoutManager gridLayoutManager = new
SpannableGridLayoutManager(new SpannableGridLayoutManager.GridSpanLookup() {
#Override
public SpannableGridLayoutManager.SpanInfo getSpanInfo(int position)
{
if (position == 0) {
return new SpannableGridLayoutManager.SpanInfo(2, 2);
//this will count of row and column you want to replace
} else {
return new SpannableGridLayoutManager.SpanInfo(1, 1);
}
}
}, 3, 1f); // 3 is the number of coloumn , how nay to display is 1f
recyclerView.setLayoutManager(gridLayoutManager);
In adapter write following Code
public MyViewHolder(View itemView) {
super(itemView);
GridLayoutManager.LayoutParams layoutParams = new
GridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
float margin = DimensionUtils.convertDpToPixel(5);
layoutParams.setMargins((int) margin, (int) margin, (int) margin,
(int) margin);
itemView.setLayoutParams(layoutParams);
}
SpannableGridLayoutManager custom class
public class SpannableGridLayoutManager extends RecyclerView.LayoutManager {
private GridSpanLookup spanLookup;
private int columns = 1;
private float cellAspectRatio = 1f;
private int cellHeight;
private int[] cellBorders;
private int firstVisiblePosition;
private int lastVisiblePosition;
private int firstVisibleRow;
private int lastVisibleRow;
private boolean forceClearOffsets;
private SparseArray<GridCell> cells;
private List<Integer> firstChildPositionForRow; // key == row, val == first child position
private int totalRows;
private final Rect itemDecorationInsets = new Rect();
public SpannableGridLayoutManager(GridSpanLookup spanLookup, int columns, float cellAspectRatio) {
this.spanLookup = spanLookup;
this.columns = columns;
this.cellAspectRatio = cellAspectRatio;
setAutoMeasureEnabled(true);
}
#Keep /* XML constructor, see RecyclerView#createLayoutManager */
public SpannableGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SpannableGridLayoutManager, defStyleAttr, defStyleRes);
columns = a.getInt(R.styleable.SpannableGridLayoutManager_android_orientation, 1);
parseAspectRatio(a.getString(R.styleable.SpannableGridLayoutManager_aspectRatio));
int orientation = a.getInt(R.styleable.SpannableGridLayoutManager_android_orientation, RecyclerView.VERTICAL);
a.recycle();
setAutoMeasureEnabled(true);
}
public interface GridSpanLookup {
SpanInfo getSpanInfo(int position);
}
public void setSpanLookup(#NonNull GridSpanLookup spanLookup) {
this.spanLookup = spanLookup;
}
public static class SpanInfo {
public int columnSpan;
public int rowSpan;
public SpanInfo(int columnSpan, int rowSpan) {
this.columnSpan = columnSpan;
this.rowSpan = rowSpan;
}
public static final SpanInfo SINGLE_CELL = new SpanInfo(1, 1);
}
public static class LayoutParams extends RecyclerView.LayoutParams {
int columnSpan;
int rowSpan;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(RecyclerView.LayoutParams source) {
super(source);
}
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
calculateWindowSize();
calculateCellPositions(recycler, state);
if (state.getItemCount() == 0) {
detachAndScrapAttachedViews(recycler);
firstVisibleRow = 0;
resetVisibleItemTracking();
return;
}
// TODO use orientationHelper
int startTop = getPaddingTop();
int scrollOffset = 0;
if (forceClearOffsets) { // see #scrollToPosition
startTop = -(firstVisibleRow * cellHeight);
forceClearOffsets = false;
} else if (getChildCount() != 0) {
scrollOffset = getDecoratedTop(getChildAt(0));
startTop = scrollOffset - (firstVisibleRow * cellHeight);
resetVisibleItemTracking();
}
detachAndScrapAttachedViews(recycler);
int row = firstVisibleRow;
int availableSpace = getHeight() - scrollOffset;
int lastItemPosition = state.getItemCount() - 1;
while (availableSpace > 0 && lastVisiblePosition < lastItemPosition) {
availableSpace -= layoutRow(row, startTop, recycler, state);
row = getNextSpannedRow(row);
}
layoutDisappearingViews(recycler, state, startTop);
}
#Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
#Override
public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
return new LayoutParams(c, attrs);
}
#Override
public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof ViewGroup.MarginLayoutParams) {
return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
} else {
return new LayoutParams(lp);
}
}
#Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
return lp instanceof LayoutParams;
}
#Override
public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
removeAllViews();
reset();
}
#Override
public boolean supportsPredictiveItemAnimations() {
return true;
}
#Override
public boolean canScrollVertically() {
return true;
}
#Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getChildCount() == 0 || dy == 0) return 0;
int scrolled;
int top = getDecoratedTop(getChildAt(0));
if (dy < 0) { // scrolling content down
if (firstVisibleRow == 0) { // at top of content
int scrollRange = -(getPaddingTop() - top);
scrolled = Math.max(dy, scrollRange);
} else {
scrolled = dy;
}
if (top - scrolled >= 0) { // new top row came on screen
int newRow = firstVisibleRow - 1;
if (newRow >= 0) {
int startOffset = top - (firstVisibleRow * cellHeight);
layoutRow(newRow, startOffset, recycler, state);
}
}
int firstPositionOfLastRow = getFirstPositionInSpannedRow(lastVisibleRow);
int lastRowTop = getDecoratedTop(
getChildAt(firstPositionOfLastRow - firstVisiblePosition));
if (lastRowTop - scrolled > getHeight()) { // last spanned row scrolled out
recycleRow(lastVisibleRow, recycler, state);
}
} else { // scrolling content up
int bottom = getDecoratedBottom(getChildAt(getChildCount() - 1));
if (lastVisiblePosition == getItemCount() - 1) { // is at end of content
int scrollRange = Math.max(bottom - getHeight() + getPaddingBottom(), 0);
scrolled = Math.min(dy, scrollRange);
} else {
scrolled = dy;
}
if ((bottom - scrolled) < getHeight()) { // new row scrolled in
int nextRow = lastVisibleRow + 1;
if (nextRow < getSpannedRowCount()) {
int startOffset = top - (firstVisibleRow * cellHeight);
layoutRow(nextRow, startOffset, recycler, state);
}
}
int lastPositionInRow = getLastPositionInSpannedRow(firstVisibleRow, state);
int bottomOfFirstRow =
getDecoratedBottom(getChildAt(lastPositionInRow - firstVisiblePosition));
if (bottomOfFirstRow - scrolled < 0) { // first spanned row scrolled out
recycleRow(firstVisibleRow, recycler, state);
}
}
offsetChildrenVertical(-scrolled);
return scrolled;
}
#Override
public void scrollToPosition(int position) {
if (position >= getItemCount()) position = getItemCount() - 1;
firstVisibleRow = getRowIndex(position);
resetVisibleItemTracking();
forceClearOffsets = true;
removeAllViews();
requestLayout();
}
#Override
public void smoothScrollToPosition(
RecyclerView recyclerView, RecyclerView.State state, int position) {
if (position >= getItemCount()) position = getItemCount() - 1;
LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) {
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
final int rowOffset = getRowIndex(targetPosition) - firstVisibleRow;
return new PointF(0, rowOffset * cellHeight);
}
};
scroller.setTargetPosition(position);
startSmoothScroll(scroller);
}
#Override
public int computeVerticalScrollRange(RecyclerView.State state) {
// TODO update this to incrementally calculate
return getSpannedRowCount() * cellHeight + getPaddingTop() + getPaddingBottom();
}
#Override
public int computeVerticalScrollExtent(RecyclerView.State state) {
return getHeight();
}
#Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
if (getChildCount() == 0) return 0;
return getPaddingTop() + (firstVisibleRow * cellHeight) - getDecoratedTop(getChildAt(0));
}
#Override
public View findViewByPosition(int position) {
if (position < firstVisiblePosition || position > lastVisiblePosition) return null;
return getChildAt(position - firstVisiblePosition);
}
public int getFirstVisibleItemPosition() {
return firstVisiblePosition;
}
private static class GridCell {
final int row;
final int rowSpan;
final int column;
final int columnSpan;
GridCell(int row, int rowSpan, int column, int columnSpan) {
this.row = row;
this.rowSpan = rowSpan;
this.column = column;
this.columnSpan = columnSpan;
}
}
/**
* This is the main layout algorithm, iterates over all items and places them into [column, row]
* cell positions. Stores this layout info for use later on. Also records the adapter position
* that each row starts at.
* <p>
* Note that if a row is spanned, then the row start position is recorded as the first cell of
* the row that the spanned cell starts in. This is to ensure that we have sufficient contiguous
* views to layout/draw a spanned row.
*/
private void calculateCellPositions(RecyclerView.Recycler recycler, RecyclerView.State state) {
final int itemCount = state.getItemCount();
cells = new SparseArray<>(itemCount);
firstChildPositionForRow = new ArrayList<>();
int row = 0;
int column = 0;
recordSpannedRowStartPosition(row, column);
int[] rowHWM = new int[columns]; // row high water mark (per column)
for (int position = 0; position < itemCount; position++) {
SpanInfo spanInfo;
int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(position);
if (adapterPosition != RecyclerView.NO_POSITION) {
spanInfo = spanLookup.getSpanInfo(adapterPosition);
} else {
// item removed from adapter, retrieve its previous span info
// as we can't get from the lookup (adapter)
spanInfo = getSpanInfoFromAttachedView(position);
}
if (spanInfo.columnSpan > columns) {
spanInfo.columnSpan = columns; // or should we throw?
}
// check horizontal space at current position else start a new row
// note that this may leave gaps in the grid; we don't backtrack to try and fit
// subsequent cells into gaps. We place the responsibility on the adapter to provide
// continuous data i.e. that would not span column boundaries to avoid gaps.
if (column + spanInfo.columnSpan > columns) {
row++;
recordSpannedRowStartPosition(row, position);
column = 0;
}
// check if this cell is already filled (by previous spanning cell)
while (rowHWM[column] > row) {
column++;
if (column + spanInfo.columnSpan > columns) {
row++;
recordSpannedRowStartPosition(row, position);
column = 0;
}
}
// by this point, cell should fit at [column, row]
cells.put(position, new GridCell(row, spanInfo.rowSpan, column, spanInfo.columnSpan));
// update the high water mark book-keeping
for (int columnsSpanned = 0; columnsSpanned < spanInfo.columnSpan; columnsSpanned++) {
rowHWM[column + columnsSpanned] = row + spanInfo.rowSpan;
}
// if we're spanning rows then record the 'first child position' as the first item
// *in the row the spanned item starts*. i.e. the position might not actually sit
// within the row but it is the earliest position we need to render in order to fill
// the requested row.
if (spanInfo.rowSpan > 1) {
int rowStartPosition = getFirstPositionInSpannedRow(row);
for (int rowsSpanned = 1; rowsSpanned < spanInfo.rowSpan; rowsSpanned++) {
int spannedRow = row + rowsSpanned;
recordSpannedRowStartPosition(spannedRow, rowStartPosition);
}
}
// increment the current position
column += spanInfo.columnSpan;
}
totalRows = rowHWM[0];
for (int i = 1; i < rowHWM.length; i++) {
if (rowHWM[i] > totalRows) {
totalRows = rowHWM[i];
}
}
}
private SpanInfo getSpanInfoFromAttachedView(int position) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (position == getPosition(child)) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
return new SpanInfo(lp.columnSpan, lp.rowSpan);
}
}
// errrrr?
return SpanInfo.SINGLE_CELL;
}
private void recordSpannedRowStartPosition(final int rowIndex, final int position) {
if (getSpannedRowCount() < (rowIndex + 1)) {
firstChildPositionForRow.add(position);
}
}
private int getRowIndex(final int position) {
return position < cells.size() ? cells.get(position).row : -1;
}
private int getSpannedRowCount() {
return firstChildPositionForRow.size();
}
private int getNextSpannedRow(int rowIndex) {
int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
int nextRow = rowIndex + 1;
while (nextRow < getSpannedRowCount()
&& getFirstPositionInSpannedRow(nextRow) == firstPositionInRow) {
nextRow++;
}
return nextRow;
}
private int getFirstPositionInSpannedRow(int rowIndex) {
return firstChildPositionForRow.get(rowIndex);
}
private int getLastPositionInSpannedRow(final int rowIndex, RecyclerView.State state) {
int nextRow = getNextSpannedRow(rowIndex);
return (nextRow != getSpannedRowCount()) ? // check if reached boundary
getFirstPositionInSpannedRow(nextRow) - 1
: state.getItemCount() - 1;
}
/**
* Lay out a given 'row'. We might actually add more that one row if the requested row contains
* a row-spanning cell. Returns the pixel height of the rows laid out.
* <p>
* To simplify logic & book-keeping, views are attached in adapter order, that is child 0 will
* always be the earliest position displayed etc.
*/
private int layoutRow(
int rowIndex, int startTop, RecyclerView.Recycler recycler, RecyclerView.State state) {
int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
int lastPositionInRow = getLastPositionInSpannedRow(rowIndex, state);
boolean containsRemovedItems = false;
int insertPosition = (rowIndex < firstVisibleRow) ? 0 : getChildCount();
for (int position = firstPositionInRow;
position <= lastPositionInRow;
position++, insertPosition++) {
View view = recycler.getViewForPosition(position);
LayoutParams lp = (LayoutParams) view.getLayoutParams();
containsRemovedItems |= lp.isItemRemoved();
GridCell cell = cells.get(position);
addView(view, insertPosition);
// TODO use orientation helper
int wSpec = getChildMeasureSpec(
cellBorders[cell.column + cell.columnSpan] - cellBorders[cell.column],
View.MeasureSpec.EXACTLY, 0, lp.width, false);
int hSpec = getChildMeasureSpec(cell.rowSpan * cellHeight,
View.MeasureSpec.EXACTLY, 0, lp.height, true);
measureChildWithDecorationsAndMargin(view, wSpec, hSpec);
int left = cellBorders[cell.column] + lp.leftMargin;
int top = startTop + (cell.row * cellHeight) + lp.topMargin;
int right = left + getDecoratedMeasuredWidth(view);
int bottom = top + getDecoratedMeasuredHeight(view);
layoutDecorated(view, left, top, right, bottom);
lp.columnSpan = cell.columnSpan;
lp.rowSpan = cell.rowSpan;
}
if (firstPositionInRow < firstVisiblePosition) {
firstVisiblePosition = firstPositionInRow;
firstVisibleRow = getRowIndex(firstVisiblePosition);
}
if (lastPositionInRow > lastVisiblePosition) {
lastVisiblePosition = lastPositionInRow;
lastVisibleRow = getRowIndex(lastVisiblePosition);
}
if (containsRemovedItems) return 0; // don't consume space for rows with disappearing items
GridCell first = cells.get(firstPositionInRow);
GridCell last = cells.get(lastPositionInRow);
return (last.row + last.rowSpan - first.row) * cellHeight;
}
/**
* Remove and recycle all items in this 'row'. If the row includes a row-spanning cell then all
* cells in the spanned rows will be removed.
*/
private void recycleRow(
int rowIndex, RecyclerView.Recycler recycler, RecyclerView.State state) {
int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
int lastPositionInRow = getLastPositionInSpannedRow(rowIndex, state);
int toRemove = lastPositionInRow;
while (toRemove >= firstPositionInRow) {
int index = toRemove - firstVisiblePosition;
removeAndRecycleViewAt(index, recycler);
toRemove--;
}
if (rowIndex == firstVisibleRow) {
firstVisiblePosition = lastPositionInRow + 1;
firstVisibleRow = getRowIndex(firstVisiblePosition);
}
if (rowIndex == lastVisibleRow) {
lastVisiblePosition = firstPositionInRow - 1;
lastVisibleRow = getRowIndex(lastVisiblePosition);
}
}
private void layoutDisappearingViews(
RecyclerView.Recycler recycler, RecyclerView.State state, int startTop) {
// TODO
}
private void calculateWindowSize() {
// TODO use OrientationHelper#getTotalSpace
int cellWidth =
(int) Math.floor((getWidth() - getPaddingLeft() - getPaddingRight()) / columns);
cellHeight = (int) Math.floor(cellWidth * (1f / cellAspectRatio));
calculateCellBorders();
}
private void reset() {
cells = null;
firstChildPositionForRow = null;
firstVisiblePosition = 0;
firstVisibleRow = 0;
lastVisiblePosition = 0;
lastVisibleRow = 0;
cellHeight = 0;
forceClearOffsets = false;
}
private void resetVisibleItemTracking() {
// maintain the firstVisibleRow but reset other state vars
// TODO make orientation agnostic
int minimumVisibleRow = getMinimumFirstVisibleRow();
if (firstVisibleRow > minimumVisibleRow) firstVisibleRow = minimumVisibleRow;
firstVisiblePosition = getFirstPositionInSpannedRow(firstVisibleRow);
lastVisibleRow = firstVisibleRow;
lastVisiblePosition = firstVisiblePosition;
}
private int getMinimumFirstVisibleRow() {
int maxDisplayedRows = (int) Math.ceil((float) getHeight() / cellHeight) + 1;
if (totalRows < maxDisplayedRows) return 0;
int minFirstRow = totalRows - maxDisplayedRows;
// adjust to spanned rows
return getRowIndex(getFirstPositionInSpannedRow(minFirstRow));
}
/* Adapted from GridLayoutManager */
private void calculateCellBorders() {
cellBorders = new int[columns + 1];
int totalSpace = getWidth() - getPaddingLeft() - getPaddingRight();
int consumedPixels = getPaddingLeft();
cellBorders[0] = consumedPixels;
int sizePerSpan = totalSpace / columns;
int sizePerSpanRemainder = totalSpace % columns;
int additionalSize = 0;
for (int i = 1; i <= columns; i++) {
int itemSize = sizePerSpan;
additionalSize += sizePerSpanRemainder;
if (additionalSize > 0 && (columns - additionalSize) < sizePerSpanRemainder) {
itemSize += 1;
additionalSize -= columns;
}
consumedPixels += itemSize;
cellBorders[i] = consumedPixels;
}
}
private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) {
calculateItemDecorationsForChild(child, itemDecorationInsets);
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + itemDecorationInsets.left,
lp.rightMargin + itemDecorationInsets.right);
heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + itemDecorationInsets.top,
lp.bottomMargin + itemDecorationInsets.bottom);
child.measure(widthSpec, heightSpec);
}
private int updateSpecWithExtra(int spec, int startInset, int endInset) {
if (startInset == 0 && endInset == 0) {
return spec;
}
int mode = View.MeasureSpec.getMode(spec);
if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
return View.MeasureSpec.makeMeasureSpec(
View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
}
return spec;
}
/* Adapted from ConstraintLayout */
private void parseAspectRatio(String aspect) {
if (aspect != null) {
int colonIndex = aspect.indexOf(':');
if (colonIndex >= 0 && colonIndex < aspect.length() - 1) {
String nominator = aspect.substring(0, colonIndex);
String denominator = aspect.substring(colonIndex + 1);
if (nominator.length() > 0 && denominator.length() > 0) {
try {
float nominatorValue = Float.parseFloat(nominator);
float denominatorValue = Float.parseFloat(denominator);
if (nominatorValue > 0 && denominatorValue > 0) {
cellAspectRatio = Math.abs(nominatorValue / denominatorValue);
return;
}
} catch (NumberFormatException e) {
// Ignore
}
}
}
}
throw new IllegalArgumentException("Could not parse aspect ratio: '" + aspect + "'");
}}
add following code to attr file
<declare-styleable name="SpannableGridLayoutManager">
<attr name="android:orientation" />
<attr name="spanCount" />
<attr name="aspectRatio" format="string" />
</declare-styleable>

A custom HorizontalScrollView,the method smoothScrollTo doesn't work

I have a custom HorizontalScrollView.But inside the class,the method smoothScrollTo doesn't work.The codes below is mine.
public class MyHorizontalScrollView extends HorizontalScrollView {
private int flagPosition;
private int scrollX;
private int windowWidth;
public MyHorizontalScrollView(Context context) {
super(context);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(outMetrics);
windowWidth = outMetrics.widthPixels;
flagPosition = windowWidth / 2;
}
public MyHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(outMetrics);
windowWidth = outMetrics.widthPixels;
flagPosition = windowWidth / 2;
}
public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(outMetrics);
windowWidth = outMetrics.widthPixels;
flagPosition = windowWidth / 2;
}
#Override
public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
super.setSmoothScrollingEnabled(true);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
scrollX = getScrollX();
break;
case MotionEvent.ACTION_UP:
scroll();
break;
}
return super.onTouchEvent(ev);
}
private void scroll() {
if (scrollX <= flagPosition/2) {
smoothScrollTo(0, 0);
} else{
smoothScrollTo(flagPosition, 0);
}
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
scrollX = getScrollX();
Log.v("my", "myscroll======+++++++onscrollchange" + scrollX);
}
}
I want a result that,I scroll the HorizontalScrollView,when I scroll to a range,then uplift my finger,the HorizontalScrollView will scroll to a specific position automaticly.
This is my code.Inside the method scroll(),I toggle smoothScrollTo.It doesn't work.But the method scrollTo worked.I don't know why.
I put the codes inside the class:
private Runnable run = new Runnable() {
#Override
public void run() {
if (scrollX <= flagPosition / 2) {
MyHorizontalScrollView.this.smoothScrollTo(0, 0);
} else {
MyHorizontalScrollView.this.smoothScrollTo(flagPosition, 0);
}
}
};
#Override
public boolean post(Runnable action) {
return super.post(action);
}
Then replace the method smoothScrollTo with post().And I realize the effect.

RecyclerView GridLayoutManager: how to auto-detect span count?

Using the new GridLayoutManager: https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager.html
It takes an explicit span count, so the problem now becomes: how do you know how many "spans" fit per row? This is a grid, after all. There should be as many spans as the RecyclerView can fit, based on measured width.
Using the old GridView, you would just set the "columnWidth" property and it would automatically detect how many columns fit. This is basically what I want to replicate for the RecyclerView:
add OnLayoutChangeListener on the RecyclerView
in this callback, inflate a single 'grid item' and measure it
spanCount = recyclerViewWidth / singleItemWidth;
This seems like pretty common behavior, so is there a simpler way that I'm not seeing?
Personaly I don't like to subclass RecyclerView for this, because for me it seems that there is GridLayoutManager's responsibility to detect span count. So after some android source code digging for RecyclerView and GridLayoutManager I wrote my own class extended GridLayoutManager that do the job:
public class GridAutofitLayoutManager extends GridLayoutManager
{
private int columnWidth;
private boolean isColumnWidthChanged = true;
private int lastWidth;
private int lastHeight;
public GridAutofitLayoutManager(#NonNull final Context context, final int columnWidth) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
public GridAutofitLayoutManager(
#NonNull final Context context,
final int columnWidth,
final int orientation,
final boolean reverseLayout) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1, orientation, reverseLayout);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
private int checkedColumnWidth(#NonNull final Context context, final int columnWidth) {
if (columnWidth <= 0) {
/* Set default columnWidth value (48dp here). It is better to move this constant
to static constant on top, but we need context to convert it to dp, so can't really
do so. */
columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
context.getResources().getDisplayMetrics());
}
return columnWidth;
}
public void setColumnWidth(final int newColumnWidth) {
if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
columnWidth = newColumnWidth;
isColumnWidthChanged = true;
}
}
#Override
public void onLayoutChildren(#NonNull final RecyclerView.Recycler recycler, #NonNull final RecyclerView.State state) {
final int width = getWidth();
final int height = getHeight();
if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
final int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = width - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = height - getPaddingTop() - getPaddingBottom();
}
final int spanCount = Math.max(1, totalSpace / columnWidth);
setSpanCount(spanCount);
isColumnWidthChanged = false;
}
lastWidth = width;
lastHeight = height;
super.onLayoutChildren(recycler, state);
}
}
I don't actually remember why I choosed to set span count in onLayoutChildren, I wrote this class some time ago. But the point is we need to do so after view get measured. so we can get it's height and width.
EDIT 1: Fix error in code caused to incorrectly setting span count. Thanks user #Elyees Abouda for reporting and suggesting solution.
EDIT 2: Some small refactoring and fix edge case with manual orientation changes handling. Thanks user #tatarize for reporting and suggesting solution.
I accomplished this using a ViewTreeObserver to get the width of the RecylcerView once rendered and then getting the fixed dimensions of my CardView from resources and then setting the span count after doing my calculations. It is only really applicable if the items you are displaying are of a fixed width. This helped me automatically populate the grid regardless of screen size or orientation.
mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int viewWidth = mRecyclerView.getMeasuredWidth();
float cardViewWidth = getActivity().getResources().getDimension(R.dimen.cardview_layout_width);
int newSpanCount = (int) Math.floor(viewWidth / cardViewWidth);
mLayoutManager.setSpanCount(newSpanCount);
mLayoutManager.requestLayout();
}
});
Well, this is what I used, fairly basic, but gets the job done for me. This code basically gets the screen width in dips and then divides by 300 (or whatever width you're using for your adapter's layout). So smaller phones with 300-500 dip width only display one column, tablets 2-3 columns etc. Simple, fuss free and without downside, as far as I can see.
Display display = getActivity().getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics();
display.getMetrics(outMetrics);
float density = getResources().getDisplayMetrics().density;
float dpWidth = outMetrics.widthPixels / density;
int columns = Math.round(dpWidth/300);
mLayoutManager = new GridLayoutManager(getActivity(),columns);
mRecyclerView.setLayoutManager(mLayoutManager);
I extended the RecyclerView and overrode the onMeasure method.
I set an item width(member variable) as early as I can,with a default of 1. This also updates on configuration changed. This will now have as many rows as can fit in portrait,landscape,phone/tablet etc.
#Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
int width = MeasureSpec.getSize(widthSpec);
if(width != 0){
int spans = width / mItemWidth;
if(spans > 0){
mLayoutManager.setSpanCount(spans);
}
}
}
A better way (imo) would be to define different span counts in (many) different values directories and let the device automatically select which span count to use. For example:
values/integers.xml -> span_count=3
values-w480dp/integers.xml -> span_count=4
values-w600dp/integers.xml -> span_count=5
I'm posting this just in case someone gets weird column width as in my case.
I'm not able to comment on #s-marks's answer due to my low reputation. I applied his solution solution but I got some weird column width, so I modified checkedColumnWidth function as follows:
private int checkedColumnWidth(Context context, int columnWidth)
{
if (columnWidth <= 0)
{
/* Set default columnWidth value (48dp here). It is better to move this constant
to static constant on top, but we need context to convert it to dp, so can't really
do so. */
columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
context.getResources().getDisplayMetrics());
}
else
{
columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, columnWidth,
context.getResources().getDisplayMetrics());
}
return columnWidth;
}
By converting the given column width into DP fixed the issue.
I conclusion above answers here
To accommodate orientation change on s-marks's answer, I added a check on width change (width from getWidth(), not column width).
private boolean mWidthChanged = true;
private int mWidth;
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
{
int width = getWidth();
int height = getHeight();
if (width != mWidth) {
mWidthChanged = true;
mWidth = width;
}
if (mColumnWidthChanged && mColumnWidth > 0 && width > 0 && height > 0
|| mWidthChanged)
{
int totalSpace;
if (getOrientation() == VERTICAL)
{
totalSpace = width - getPaddingRight() - getPaddingLeft();
}
else
{
totalSpace = height - getPaddingTop() - getPaddingBottom();
}
int spanCount = Math.max(1, totalSpace / mColumnWidth);
setSpanCount(spanCount);
mColumnWidthChanged = false;
mWidthChanged = false;
}
super.onLayoutChildren(recycler, state);
}
The upvoted solution is fine, but handles the incoming values as pixels, which can trip you up if you're hardcoding values for testing and assuming dp. Easiest way is probably to put the column width in a dimension and read it when configuring the GridAutofitLayoutManager, which will automatically convert dp to correct pixel value:
new GridAutofitLayoutManager(getActivity(), (int)getActivity().getResources().getDimension(R.dimen.card_width))
Set minimal fixed width of imageView (144dp x 144dp for example)
When you create GridLayoutManager, you need to know how much columns will be with minimal size of imageView:
WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); //Получаем размер экрана
Display display = wm.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
int screenWidth = point.x; //Ширина экрана
int photoWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 144, this.getResources().getDisplayMetrics()); //Переводим в точки
int columnsCount = screenWidth/photoWidth; //Число столбцов
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, columnsCount);
recyclerView.setLayoutManager(gridLayoutManager);
After that you need to resize imageView in adapter if you have space in column. You may send newImageViewSize then inisilize adapter from activity there you calculate screen and column count:
#Override //Заполнение нашей плитки
public void onBindViewHolder(PhotoHolder holder, int position) {
...
ViewGroup.LayoutParams photoParams = holder.photo.getLayoutParams(); //Параметры нашей фотографии
int newImageViewSize = screenWidth/columnsCount; //Новый размер фотографии
photoParams.width = newImageViewSize; //Установка нового размера
photoParams.height = newImageViewSize;
holder.photo.setLayoutParams(photoParams); //Установка параметров
...
}
It works in both orientations. In vertical I have 2 columns and in horizontal - 4 columns. The result: https://i.stack.imgur.com/WHvyD.jpg
This is s.maks' class with a minor fix for when the recyclerview itself changes size. Such as when you deal with the orientation changes yourself (in the manifest android:configChanges="orientation|screenSize|keyboardHidden"), or some other reason the recyclerview might change size without the mColumnWidth changing. I also changed the int value it takes to be the resource of the size and allowed a constructor of no resource then setColumnWidth to do that yourself.
public class GridAutofitLayoutManager extends GridLayoutManager {
private Context context;
private float mColumnWidth;
private float currentColumnWidth = -1;
private int currentWidth = -1;
private int currentHeight = -1;
public GridAutofitLayoutManager(Context context) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1);
this.context = context;
setColumnWidthByResource(-1);
}
public GridAutofitLayoutManager(Context context, int resource) {
this(context);
this.context = context;
setColumnWidthByResource(resource);
}
public GridAutofitLayoutManager(Context context, int resource, int orientation, boolean reverseLayout) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1, orientation, reverseLayout);
this.context = context;
setColumnWidthByResource(resource);
}
public void setColumnWidthByResource(int resource) {
if (resource >= 0) {
mColumnWidth = context.getResources().getDimension(resource);
} else {
/* Set default columnWidth value (48dp here). It is better to move this constant
to static constant on top, but we need context to convert it to dp, so can't really
do so. */
mColumnWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
context.getResources().getDisplayMetrics());
}
}
public void setColumnWidth(float newColumnWidth) {
mColumnWidth = newColumnWidth;
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
recalculateSpanCount();
super.onLayoutChildren(recycler, state);
}
public void recalculateSpanCount() {
int width = getWidth();
if (width <= 0) return;
int height = getHeight();
if (height <= 0) return;
if (mColumnWidth <= 0) return;
if ((width != currentWidth) || (height != currentHeight) || (mColumnWidth != currentColumnWidth)) {
int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = width - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = height - getPaddingTop() - getPaddingBottom();
}
int spanCount = (int) Math.max(1, Math.floor(totalSpace / mColumnWidth));
setSpanCount(spanCount);
currentColumnWidth = mColumnWidth;
currentWidth = width;
currentHeight = height;
}
}
}
I like s.maks' answer but I found another edge case: If you set the height of the RecyclerView to WRAP_CONTENT it may happen that the height of the recyclerview is calculated incorrectly based on an outdated spanCount value. The solution I found is a small modification of the proposed onLayoutChildren() method:
public void onLayoutChildren(#NonNull final RecyclerView.Recycler recycler, #NonNull final RecyclerView.State state) {
final int width = getWidth();
final int height = getHeight();
if (columnWidth > 0 && (width > 0 || getOrientation() == HORIZONTAL) && (height > 0 || getOrientation() == VERTICAL) && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
final int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = width - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = height - getPaddingTop() - getPaddingBottom();
}
final int spanCount = Math.max(1, totalSpace / columnWidth);
if (getSpanCount() != spanCount) {
setSpanCount(spanCount);
}
isColumnWidthChanged = false;
}
lastWidth = width;
lastHeight = height;
super.onLayoutChildren(recycler, state);
}
Set spanCount to a large number (which is the max number of column) and set a custom SpanSizeLookup to the GridLayoutManager.
mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int i) {
return SPAN_COUNT / (int) (mRecyclerView.getMeasuredWidth()/ CELL_SIZE_IN_PX);
}
});
It's a bit ugly, but it work.
I think a manager like AutoSpanGridLayoutManager would be the best solution, but i didn't find anything like that.
EDIT : There is a bug, on some device it add blank space to the right
Here's the relevant parts of a wrapper I've been using to auto-detect the span count. You initialize it by calling setGridLayoutManager with a R.layout.my_grid_item reference, and it figures out how many of those can fit on each row.
public class AutoSpanRecyclerView extends RecyclerView {
private int m_gridMinSpans;
private int m_gridItemLayoutId;
private LayoutRequester m_layoutRequester = new LayoutRequester();
public void setGridLayoutManager( int orientation, int itemLayoutId, int minSpans ) {
GridLayoutManager layoutManager = new GridLayoutManager( getContext(), 2, orientation, false );
m_gridItemLayoutId = itemLayoutId;
m_gridMinSpans = minSpans;
setLayoutManager( layoutManager );
}
#Override
protected void onLayout( boolean changed, int left, int top, int right, int bottom ) {
super.onLayout( changed, left, top, right, bottom );
if( changed ) {
LayoutManager layoutManager = getLayoutManager();
if( layoutManager instanceof GridLayoutManager ) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
LayoutInflater inflater = LayoutInflater.from( getContext() );
View item = inflater.inflate( m_gridItemLayoutId, this, false );
int measureSpec = View.MeasureSpec.makeMeasureSpec( 0, View.MeasureSpec.UNSPECIFIED );
item.measure( measureSpec, measureSpec );
int itemWidth = item.getMeasuredWidth();
int recyclerViewWidth = getMeasuredWidth();
int spanCount = Math.max( m_gridMinSpans, recyclerViewWidth / itemWidth );
gridLayoutManager.setSpanCount( spanCount );
// if you call requestLayout() right here, you'll get ArrayIndexOutOfBoundsException when scrolling
post( m_layoutRequester );
}
}
}
private class LayoutRequester implements Runnable {
#Override
public void run() {
requestLayout();
}
}
}

Android Spannabe Emoji loads slow in GridView and TextView

I am making a little chat in my Android app and I am using emojis (emoticons) that displays in EditText and TextView with SpannableString.
For that I made a class (code is below). Also I made a gridview which loads all the emojis. The problem is that all works is very slow (because I have 500 emojis) It takes a lot to load and display the emojis. Below is the code I'm using.
I am looking for a better algorithm to replace the String with an emoji or another way load the emojis faster.
public class EmoticonHandler {
private static final Map<String, Integer> emoticons = new HashMap<String, Integer>();
private static void addPattern(Map<String, Integer> map, String smile,
int resource) {
map.put(smile, resource);
}
// Add the items to the HasMap
static {
// Smileys
addPattern(emoticons, "#ce001#", R.drawable._ce001_);
addPattern(emoticons, "#ce002#", R.drawable._ce002_);
addPattern(emoticons, "#ce003#", R.drawable._ce003_);
addPattern(emoticons, "#ce004#", R.drawable._ce004_);
// Here comes the other 500 emojis
}
// Get image for each text smiles
public static void getSmiledText(Context context, Spannable span, int size) {
int index;
for (index = 0; index < span.length(); index++) {
for (Entry<String, Integer> entry : emoticons.entrySet()) {
int length = entry.getKey().length();
if (index + length > span.length())
continue;
if (span.subSequence(index, index + length).toString()
.equals(entry.getKey())) {
span.setSpan(new EmoticonSpan(context, entry.getValue(),
size), index, index + length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
index += length - 1;
break;
}
}
}
}}
Here is the code for the EmoticonSpan
public class EmoticonSpan extends DynamicDrawableSpan {
private Context context;
private int resourceID;
private int size;
private Drawable drawable;
public EmoticonSpan(Context context, int resourceID, int size) {
super();
this.context = context;
this.resourceID = resourceID;
this.size = size;
}
#Override
public Drawable getDrawable() {
if (drawable == null) {
try {
drawable = context.getResources().getDrawable(resourceID);
drawable.setBounds(0, 0, size, size);
} catch (Exception e) {
// Swallow
}
}
return drawable;
}
}
You can use android guide recommendation to display grid view images smoothly and off your main up thread.
http://developer.android.com/training/displaying-bitmaps/display-bitmap.html#gridview
I had a similar problem. Ended up using Emoji maps. Just cram Emojis on a (single) image of no more than 2048X2048 (the biggest Android can handle), and show that as the input grid. Then calculate the X/Y position and find out which Emoji was pointed by the user.
Just remember to take into consideration different device sizes (mdpi ... xxxdpi), and calculate position with correct scroll, and resize offsets.
Here is good code for you :
public class EmojiKeyboard {
private static final String TAG = "EmojiKeyboard";
private static final String PREF_KEY_HEIGHT_KB = "EmojiKbHeight";
private Context context;
private int screenHeight = -1;
private int emojiKbHeight = -1;
private PopupWindow emojiKeyboardPopup;
private View view;
private SharedPreferences preferences;
public EmojiKeyboard(Context context, View view) {
if (context instanceof Activity) {
this.context = context;
this.view = view;
preferences = context.getSharedPreferences(context.getString(R.string.app_name), Context.MODE_PRIVATE);
//Restore EmojiKeyboard Height
emojiKbHeight = preferences.getInt(PREF_KEY_HEIGHT_KB, -1);
//TODO support less then 11 API, and not perfect resizing when switched the keyboard
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/*
* Get root view height
* */
screenHeight = screenHeight == -1 && bottom > oldBottom
? bottom
: screenHeight;
/*
* Calculate soft keyboard height
* */
int dHeight = oldBottom - bottom;
boolean validHeight = emojiKbHeight == -1 && dHeight > 80 && bottom != oldBottom;
/*
* Сheck twice because the keyboard may have been switched
* */
emojiKbHeight = validHeight
? dHeight : emojiKbHeight != (dHeight) && dHeight > 0
? dHeight
: emojiKbHeight;
/*
* Store emoji keyboard height into SharedPreferences
* */
preferences.edit().putInt(PREF_KEY_HEIGHT_KB, emojiKbHeight).commit();
/*
* If layout returned to a standard height then dismissing keyboard (OnBackPressed)
* */
if (screenHeight == bottom) {
dismissEmojiKeyboard();
}
/*
* Resize emoji on the go when a user switches between keyboards
* */
resizeEmoji();
}
});
}
}
public void showEmoji() {
if (emojiKeyboardPopup == null) {
createEmojiKeyboard();
}
if (!isShowed()) {
new Handler().postDelayed(new Runnable() {
public void run() {
emojiKeyboardPopup.showAtLocation(view, Gravity.BOTTOM, 0, 0);
resizeEmoji();
}
}, 10L);
} else {
dismissEmojiKeyboard();
}
}
public void createEmojiKeyboard() {
EmojiView emojiKeyboard = new EmojiView(context, EmojiView.EMOJI_DARK_STYLE, new EmojiView.onEmojiClickListener() {
public void onBackspace() {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
((Activity) context).getWindow().getCurrentFocus().dispatchKeyEvent(new KeyEvent(0, 67));
}
}
public void onEmojiSelected(Emojicon emojicon) {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
EmojiView.input((EditText) ((Activity) context).getWindow().getCurrentFocus(), emojicon);
}
}
});
emojiKeyboardPopup = new PopupWindow(emojiKeyboard);
emojiKeyboardPopup.setHeight(View.MeasureSpec.makeMeasureSpec(setEmojiKeyboardHeight(), View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setWidth(View.MeasureSpec.makeMeasureSpec(getDisplayDimensions(context).x, View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setAnimationStyle(0);
}
public void dismissEmojiKeyboard() {
if (isShowed()) {
emojiKeyboardPopup.dismiss();
}
}
public boolean isShowed() {
return emojiKeyboardPopup != null && emojiKeyboardPopup.isShowing();
}
/*
* Emoji set up size
* */
public void resizeEmoji() {
if (isShowed()) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiKeyboardPopup.getContentView().getLayoutParams();
layoutParams.height = setEmojiKeyboardHeight();
wm.updateViewLayout(emojiKeyboardPopup.getContentView(), layoutParams);
}
}
public int setEmojiKeyboardHeight() {
return emojiKbHeight == -1 && emojiKbHeight != screenHeight && emojiKbHeight < 80
? (getDisplayDimensions(context).y / 2)
: emojiKbHeight;
}
public Point getDisplayDimensions(Context context) {
Point size = new Point();
WindowManager w = ((Activity) context).getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
w.getDefaultDisplay().getSize(size);
} else {
Display d = w.getDefaultDisplay();
size.x = d.getWidth();
size.y = d.getHeight();
}
return size;
}
}

Not getting screen Height and widht in Android

I using both Using Activity or without Activity, Not getting both height and width in Android.Please correct me, What i am doing wrong.
Here With Activity:
public class LandPort extends Activity{
public final static int height = 0;
public final static int width = 0;
Context context;
DisplayMetrics displaymetrics = new DisplayMetrics();
public LandPort(Context context) {
this.context = context;
}
public int getHeight() {
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
return height;
}
public int getWidth() {
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int width = displaymetrics.widthPixels;
return width;
}
}
In MainActivity:
LandPort lp= new LandPort(getApplicationContext());
int height = lp.getHeight();
int width =lp.getWidth();
Getting height and width both 0;
Another Without Activity:
public class GetHeigthWidth {
public final static int height = 0;
public final static int widht = 0;
Context context;
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
public GetHeigthWidth(Context context) {
this.context = context;
}
public int getHeight() {
#SuppressWarnings("deprecation")
int height = display.getHeight();
return height;
}
public int getWidth() {
#SuppressWarnings("deprecation")
int widht = display.getWidth();
return widht;
}
}
From MainActivty:
GetHeigthWidth ghw= new GetHeigthWidth(getApplicationContext());
int height = ghw.getHeight();
int width =ghw.getWidth();
Aslo get Height and Width 0.
So, How to get Actual Height and width in this>
call your getHeight(); getWidth() stuff in
int height;
int width;
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(globalListner);
OnGlobalLayoutListener globalListner = new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
height = view.getHeight();
widht = view.getWidth();
}
});
and dont forget to remove that global listner at the End :)
Try this
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
setContentView(R.layout.yourLayout);
}
you need to initialize your activity.
UPDATE
You need to do this:
import android.content.Context;
import android.view.Display;
import android.view.WindowManager;
public class GetHeigthWidth {
public final static int height = 0;
public final static int widht = 0;
Context context;
WindowManager wm;
Display display;
public GetHeigthWidth(Context context) {
this.context = context;
wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
display = wm.getDefaultDisplay();
}
public int getHeight() {
#SuppressWarnings("deprecation")
int height = display.getHeight();
return height;
}
public int getWidth() {
#SuppressWarnings("deprecation")
int widht = display.getWidth();
return widht;
}
}
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
int width = displaymetrics.widthPixels;

Categories

Resources