Determining the entry shown is listview - android

i am trying to send data in a listview when a button is clicked.
However my listview show 2 row on at once one full row and one partial row . Is there a way i can determine which row is showing partial and which is showing fully.
I am able to get the index that is showing only. is there another approach ?
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE){
Rect r = new Rect ();
View child = recordListview.getChildAt(view.getFirstVisiblePosition()); // first visible child
if (child == null)
return;
double height = child.getHeight () * 1.0;
recordListview.getChildVisibleRect (child, r, null);
Log.d("Visible1 ", view.getFirstVisiblePosition() + " " + height + " " + r.height() );
if (Math.abs (r.height ()) < height / 2.0) {
// show next child
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition()+1);
Log.d("Visible1 Location", view.getFirstVisiblePosition() +1+ "");
}
else {
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition());
Log.d("Visible1 Location", view.getFirstVisiblePosition()+ "");
}
}
}
});

Seems You've understood documentation of getChildVisibleRect() incorrectly.
It mentions:
r The input rectangle, defined in the child coordinate system. Will
be overwritten to contain the resulting visible rectangle, expressed
in global (root) coordinates
So, if You're providing empty rectangle in the child coordinate then it can be translated only into empty visible rectagle, right?
For me this code seems to work:
recordListview.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
final View child = recordListview.getChildAt(view.getFirstVisiblePosition());
if (child == null) {
return;
}
final Rect r = new Rect (0, 0, child.getWidth(), child.getHeight());
final double height = child.getHeight () * 1.0;
recordListview.getChildVisibleRect(child, r, null);
Log.d("Visible1 ", view.getFirstVisiblePosition() + " " + height + " " + r.height());
if (Math.abs (r.height ()) < height / 2.0) {
// show next child
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition()+1);
Log.d("Visible1 Location", view.getFirstVisiblePosition() +1+ "");
} else {
recordListview.smoothScrollToPosition(view.getFirstVisiblePosition());
Log.d("Visible1 Location", view.getFirstVisiblePosition()+ "");
}
}
}
#Override
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) {
// nothing to do here
}
});
Regarding initial question about determining which view is visible fully and which is not, I would suggest to use the following code:
#Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
final int firstVisiblePosition = view.getFirstVisiblePosition();
View child = recordListview.getChildAt(firstVisiblePosition);
if (child == null) {
return;
}
if (mListItemsOnScreen == 0) {
// number of total visible items, including items which are not fully visible
mListItemsOnScreen = (int) Math.ceil(((double)recordListview.getHeight()) / (child.getHeight() + recordListview.getDividerHeight()));
}
final Rect r = new Rect(0, 0, child.getWidth(), child.getHeight());
final double height = child.getHeight();
recordListview.getChildVisibleRect(child, r, null);
Log.d("Visible1", " items till " + firstVisiblePosition + " are not visible");
// Check top item
Log.d("Visible1", firstVisiblePosition + " is visible " + (r.height() >= height ? " fully" : "partially"));
// check bottom item
child = recordListview.getChildAt(firstVisiblePosition + mListItemsOnScreen);
if (child != null) {
r.set(0, 0, child.getWidth(), child.getHeight());
recordListview.getChildVisibleRect(child, r, null);
Log.d("Visible1", " items from " + firstVisiblePosition + " till " + (firstVisiblePosition + mListItemsOnScreen) + " are fully visible");
Log.d("Visible1", (firstVisiblePosition + mListItemsOnScreen) + " is visible " + (r.height() >= height ? " fully" : "partially"));
} else {
Log.d("Visible1", " items from " + firstVisiblePosition + " till " + (firstVisiblePosition + mListItemsOnScreen) + " are fully visible");
Log.d("Visible1", (firstVisiblePosition + mListItemsOnScreen) + " is invisible ");
}
}
}

Related

Implementing recyclerview holder view size change when scrolling another recyclerview

I have in my app a fragment that has 2 Recyclerview - one horizontal and one vertical.
I am trying to implement an "animation" that changes the horizontal RV's views height and width according to my vertical RV's scroll position.
Here is my implementation of NestedScrollView.OnScrollChangeListener() -
featuredProductsNestedScrollView.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener)
(nestedScrollView, scrollX, scrollY, oldScrollX, oldScrollY) -> {
Log.d("scrollYValue", "scrollY - " + scrollY);
Log.d("scrollYValue", "oldScrollY - " + oldScrollY);
// Log.d("scrollYValue", "-----------------");
int calculatedValueFrom = scrollY;
if (scrollY == 0)
calculatedValueFrom = oldScrollY;
else if (oldScrollY == 0)
return;
// if (scrollY > oldScrollY) {
// calculatedValueFrom = scrollY;
// } else {
// calculatedValueFrom = oldScrollY;
// }
// Log.d("scrollYValue", "calculatedValueFrom - " + calculatedValueFrom);
int vendorsRowItemHeight = Math.max(160 - calculatedValueFrom, 80);
// Log.d("scrollYValue", "vendorsRowItemHeight - " + vendorsRowItemHeight);
// Log.d("scrollYValue", "scrollY - " + scrollY);
int newWidth = (int) (303 * (vendorsRowItemHeight / 160f));
int newHeight = vendorsRowItemHeight - 10;
// Log.d("scrollYValue", "newWidth - " + newWidth);
// Log.d("scrollYValue", "newHeight - " + newHeight);
// Log.d("scrollYValue", "-----------------");
int newHeight1 = newHeight * 5;
int newWidth1 = newWidth * 5;
// Log.d("scrollYValue", "newHeight1 - " + newHeight1);
// Log.d("scrollYValue", "-----------------");
// Log.d("scrollYValue", "newWidth1 - " + newWidth1);
// Log.d("scrollYValue", "-----------------");
resizeView(vendorsList, ViewGroup.LayoutParams.MATCH_PARENT, newHeight1);
resizeView(featuredVendorsNestedScrollView, ViewGroup.LayoutParams.MATCH_PARENT, newHeight1);
for (int i = 0; i < currentVendorsList.size(); i++) {
VendorsHolder holder = (VendorsHolder) vendorsList.findViewHolderForAdapterPosition(i);
if (holder != null) {
resizeView(holder.vendorHolderCardview, newWidth1, newHeight1);
vendorsAdapter.notifyDataSetChanged();
}
}
});
private void resizeView(View view, int newWidth, int newHeight) {
try {
Constructor<? extends ViewGroup.LayoutParams> ctor = view.getLayoutParams().getClass().getDeclaredConstructor(int.class, int.class);
view.setLayoutParams(ctor.newInstance(newWidth, newHeight));
} catch (Exception e) {
e.printStackTrace();
}
}
I have some weird behaviour going on in my app when this function works - it does resize the top horizontal RV's views, but for every scroll being made the scrollY value gets rested to 0 so the animation has the feeling of a flickering animation (which is actually not true, it actually does what I wanted but for some reason goes back to the minimal value possible)
here is an image of my log prints of scrollY so you can better understand what I mean -
So basically, for some reason, the scrollY value represents something other than just the value going down...otherwise how can I explain why it resets itself for every time the function is being called?

Disable central item overlap in RecyclerView

I have horizontal RecyclerView with images.
When i scroll it left, i have image order like this.
When i scroll it right, i have order like this.
I need to set central image on top, above left and right ones. How to do it?
I overrided getChildDrawingOrder method:
#Override
protected int getChildDrawingOrder(int childCount, int i) {
int centerChild;
//find center row
if ((childCount % 2) == 0) { //even childCount number
centerChild = childCount / 2; // if childCount 8 (actualy 0 - 7), then 4 and 4-1 = 3 is in centre.
int otherCenterChild = centerChild - 1;
//Which more in center?
View child = this.getChildAt(centerChild);
final int left = child.getLeft();
final int right = child.getRight();
//if this row goes through center then this
final int absParentCenterX = getLeft() + getWidth() / 2;
//Log.i("even", i + " from " + (childCount - 1) + ", while centerChild = " + centerChild);
if ((left < absParentCenterX) && (right > absParentCenterX)) {
//this child is in center line, so it is last
//centerChild is in center, no need to change
} else {
centerChild = otherCenterChild;
}
} else {//not even - done
centerChild = childCount / 2;
//Log.i("not even", i + " from " + (childCount - 1) + ", while centerChild = " + centerChild);
}
int rez;
//find drawIndex by centerChild
if (i > centerChild) {
//below center
rez = (childCount - 1) - i + centerChild;
} else if (i == centerChild) {
//center row
//draw it last
rez = childCount - 1;
} else {
//above center - draw as always
// i < centerChild
rez = i;
}
//Log.i("return", "" + rez);
return rez;
}
Also i set
setChildrenDrawingOrderEnabled(true);
Full code is here.
Fixed with adding ViewCompat.setTranslationZ(view, translationZ);
protected final void updateViews() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
//setMarginsForChild(child);
if (mAppearAnimationIsWorking)
child.setAlpha(0);
else
child.setAlpha(1);
if (mScaleUnfocusedViews && !mAppearAnimationIsWorking) {
float scale = computeScale(child);
child.setTranslationY(-getMeasuredHeight() * (1 - scaleFactor) / 2);
ViewCompat.setTranslationZ(child, -getPercentageFromCenter(child));
child.setScaleX(scale * scaleFactor * innerScale);
child.setScaleY(scale * scaleFactor * innerScale);
}
}
}
getChildDrawingOrder and setChildrenDrawingOrderEnabled can be removed.

ViewGroup Errors

So, for practice, i'm learning how to programatically create views. I've created a new layout extending Viewgroup (which I called Custom1) which puts children views (all of the same size) in two columns.
The children of this group are also a custom layout (which I called Custom2) containing an imageview, and two textviews. I use a for loop to add the necessary amount of views to the viewgroup, and the onLayout is overriden.
Now, I tried to run this on a Nexus 4 with the "show layout bounds" option checked. I can see the bounds of the children of the Custom1 are all in the right place, and based on the log, the children of custom2 are also in the correct place. However, only the first "custom2" is showing properly (I.e. the first custom2 shows an imageView and two textviews, the rest are empty).
Is it possible that the parent view is covering up the children views?
If not, has anybody run into a similar issue before?
Here is some of my code for custom1:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
if (ItemToDebug.equals("Layout")){
if (count == numberOfChannels){
Log.d("Layout:", "Number of children matches number of channels");
}else{
Log.d("Layout:", "Mismatch between number of children and number of channels");
}
Log.d("Layout:", "onLayout " + Integer.toString(count) + " children");
}
for (int i = 0; i < count; i++){
View child = getChildAt(i);
if (i%2 == 0){
if (LayoutLeftChild(i/2, l, t, r, b, child)){ //Lays out Child, returning a Boolean if successful
if (ItemToDebug.equals("Layout")){
Log.d("Layout:", "onLayoutLeftChild number " + Integer.toString(i/2) + "successful");
}
} else
if (ItemToDebug.equals("Layout")){
Log.d("Layout:", "onLayoutLeftChild number " + Integer.toString(i/2) + "failed");
}
}
if (i%2 == 1){
if (LayoutRightChild(i/2, l, t, r, b, child)){
if (ItemToDebug.equals("Layout")){
Log.d("Layout:", "onLayoutRightChild number " + Integer.toString(i/2) + "successful");
}
}else{
if (ItemToDebug.equals("Layout")){
Log.d("Layout:", "onLayoutRightChild number " + Integer.toString(i/2) + "failed");
}
}
}
}
}
/*
Left edge for right column = l + (r-l)/2 + 15
Right edge for right column = r - 20
20dp Margin between rows
Rows are 400 dp tall
*/
private boolean LayoutRightChild(int i, int l, int t, int r, int b, View child) {
final View Child = child;
final int Left = l + (r-l)/2 + 15;
final int Right = r - 20;
final int Top = t + i*20 + (i-1)*400;
final int Bottom = Top + 400;
Child.layout(Left, Top, Right, Bottom);
if (ItemToDebug.equals("Layout")){
Log.d("Layout:", "Child laid out at (" + Integer.toString(Left) + ", " + Integer.toString(Top) + ", " + Integer.toString(Right) + ", " + Integer.toString(Bottom) + ")");
}
return true;
}
/*
Left edge for left column = l + 20
Right edge for left column = l + (r-l)/2 - 15
20dp Margin between rows
Rows are 400 dp tall
*/
private boolean LayoutLeftChild(int i, int l, int t, int r, int b, View child) {
final View Child = child;
final int Left = l + 20;
final int Right = l + (r-l)/2 - 15;
final int Top = t + i*20 + (i-1)*400;
final int Bottom = Top + 400;
Child.layout(Left, Top, Right, Bottom);
if (ItemToDebug.equals("Layout")){
Log.d("Layout:", "Child laid out at (" + Integer.toString(Left) + ", " + Integer.toString(Top) + ", " + Integer.toString(Right) + ", " + Integer.toString(Bottom) + ")");
}
return true;
}
Here is some code from custom2:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount(); //There should be three children - one ImageView on top and two TextViews on bottom
if (ItemToDebug.equals("Layout")){
if (count == 3){
Log.d("View Contents:", "3 Children Views found");
}else{
Log.d("View Contents:", "Number of Children Incorrect. " + Integer.toString(count) + " children found.");
}
Log.d("Layout:", "onLayout " + Integer.toString(count) + " children");
}
//Get children here in for loop and place.
for (int i = 0; i < count; i++){
final View child = this.getChildAt(i);
//Layout should already have margins, so align contents with left and right sides.
int width = r - l;
int top;
int height;
switch(i){
case 0:
top = t;
height = 100;
if (ItemToDebug.equals("Layout")) {
Log.d("Layout:", "Image Laid out at (" + Integer.toString(l) + ", " + Integer.toString(top) + ", " + Integer.toString(r) + ", " + Integer.toString(top + height) + ")");
}
break;
case 1:
top = t + 100;
height = 60;
if (ItemToDebug.equals("Layout")) {
Log.d("Layout:", "TextView (nowPlaying) Laid out at (" + Integer.toString(l) + ", " + Integer.toString(top) + ", " + Integer.toString(r) + ", " + Integer.toString(top + height) + ")");
}
break;
case 2:
top = t + 160;
height = 60;
if (ItemToDebug.equals("Layout")) {
Log.d("Layout:", "TextView (nextPlaying) Laid Out at (" + Integer.toString(l) + ", " + Integer.toString(top) + ", " + Integer.toString(r) + ", " + Integer.toString(top + height) + ")");
}
break;
default:
top = t;
height = 0;
if (ItemToDebug.equals("Layout")){
Log.d("Layout:", "More than 3 children have been added to the Custom2");
}
break;
}
child.layout(l, top, r, top + height);
}
}
Here is my log:
https://drive.google.com/file/d/0B0EGM_1_9jazWEZ1RXh4a2VXdnM/view?usp=sharing
And the screenshot.
The answer can be found here: Views within a Custom ViewGroup are not showing
Children are laid our wrt their parents (left and top are 0)

Android layout not aligned parent as instructed in java code

I am having problem inflating my CustomView programmatically from the XML layout that I specify.
I have a CustomView which extends RelativeLayout and contains another RelativeLayout which in turns contain 2 ImageView and 1 LinearLayout. The ImageViews are arrow Icons which I put to the left and right of the parent by android:layout_alignParentLeft="true" and android:layout_alignParentRight="true" respectively, and the LinearLayout is used to fill all the space in between.
To make it clear, here is the xml layout view in the Eclipse Layout Designer, which is what I intended it to be...
If I setContentView(R.layout.my_xml_layout); directly from the Activity, everything appears as shown in Eclipse Layout Designer, however, if I inflate the R.layout.my_xml_layout from my CustomView's constructor, there is a stubborn margin to the left and right of the ImageView that cannot go away.
This is done in java code, and is problematic:
Any help will be highly appreciated! Thanks in advance!
my_xml_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<ImageView
android:id="#+id/imageLeftArrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="#drawable/dock_leftarrow" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</LinearLayout>
<ImageView
android:id="#+id/imageRightArrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="#drawable/dock_rightarrow" />
</RelativeLayout>
</RelativeLayout>
I inflate it in the CustomView's Constructor through this line:
View.inflate( mContext, R.layout.my_xml_layout, this );
My CustomView's onLayout:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Do nothing. Do not call the superclass method--that would start a layout pass
// on this view's children. PieChart lays out its children in onSizeChanged().
Log.e("DrawView", "DrawView.onLayout: " + l + ", " + t + ", " + r + ", " + b);
int iChildCount = this.getChildCount();
for ( int i = 0; i < iChildCount; i++ ) {
View pChild = this.getChildAt(i);
pChild.layout(0, 0, pChild.getMeasuredWidth(), pChild.getMeasuredHeight());
}
}
My CustomView's onMeasure:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Try for a width based on our minimum
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d("DockView", "DockView.onMeasure: width: " + widthMeasureSpec + " getWidth: " + MeasureSpec.getSize(widthMeasureSpec));
Log.d("DockView", "DockView.onMeasure: height: " + heightMeasureSpec + " getHeight: " + MeasureSpec.getSize(heightMeasureSpec));
Log.d("DockView", "DockView.onMeasure: getPaddingLeft: " + getPaddingLeft() + " getPaddingRight: " + getPaddingRight());
Log.d("DockView", "DockView.onMeasure: getPaddingTop: " + getPaddingTop() + " getPaddingBottom: " + getPaddingBottom());
// http://stackoverflow.com/a/17545273/474330
int iParentWidth = MeasureSpec.getSize(widthMeasureSpec);
int iParentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(iParentWidth, iParentHeight);
int iChildCount = this.getChildCount();
for ( int i = 0; i < iChildCount; i++ ) {
View pChild = this.getChildAt(i);
this.measureChild( pChild,
MeasureSpec.makeMeasureSpec(iParentWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(iParentHeight, MeasureSpec.EXACTLY)
);
}
}
For the time being, I am resorting to a hack.
I only add the LinearLayout as subview of my CustomView. Then I manually render the two ImageViews in onDraw(Canvas c); function of my CustomView. And in order to get the LinearLayout to fit into the remaining space between the two ImageViews, I calculate the margin of the LinearLayout in my CustomView's onLayout.
horizontal_dock_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#android:color/white" >
<ImageView
android:id="#+id/imageLauncher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_launcher"/>
</LinearLayout>
</RelativeLayout>
CustomView.java
/**
* Initialize the control. This code is in a separate method so that it can be
* called from both constructors.
*/
private void init() {
setWillNotDraw( false );
// Load the arrow bitmap
mArrowBitmap = ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.dock_leftarrow)).getBitmap();
ViewGroup pRootView = (ViewGroup) View.inflate( mContext, R.layout.horizontal_dock_view, this );
Log.d("DockView", "DockView.init: " + pRootView.getClass().getCanonicalName());
mIconContainerView = (LinearLayout) ((RelativeLayout)pRootView.getChildAt(0)).getChildAt(0);
Log.d("DockView", "DockView.init: " + mIconContainerView.getClass().getCanonicalName());
// if ( mArrowBitmap != null ) {
// // set the icon container margin
// float fWidth = this.getWidth(); // View's width
// float fHeight = this.getHeight(); // View's height
// float fScale = fHeight / mArrowBitmap.getHeight();
// float fArrowWidth = mArrowBitmap.getWidth() * fScale;
// float fArrowHeight = mArrowBitmap.getHeight() * fScale;
// Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
// ((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
// }
}
CustomView.onLayout:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Do nothing. Do not call the superclass method--that would start a layout pass
// on this view's children. PieChart lays out its children in onSizeChanged().
Log.e("DrawView", "DrawView.onLayout: " + l + ", " + t + ", " + r + ", " + b);
if ( mIconContainerView != null && mArrowBitmap != null ) {
// set the icon container margin
float fHeight = this.getHeight();
float fScale = fHeight / mArrowBitmap.getHeight();
float fArrowWidth = mArrowBitmap.getWidth() * fScale;
float fArrowHeight = mArrowBitmap.getHeight() * fScale;
Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
this.requestLayout();
}
int iChildCount = this.getChildCount();
for ( int i = 0; i < iChildCount; i++ ) {
View pChild = this.getChildAt(i);
pChild.layout(0, 0, pChild.getMeasuredWidth(), pChild.getMeasuredHeight());
}
}
CustomView.onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// mBgColor = Color.CYAN;
// Log.e("DockView", "DockView.onDraw: " + mBgColor);
Log.e("DockView", "DockView.onDraw: width: " + this.getWidth() + " height: " + this.getHeight());
// debugChildren( (RelativeLayout) ((RelativeLayout)this.getChildAt(0)).getChildAt(0) );
debugChildren( ((RelativeLayout)this.getChildAt(0)) );
// draw the background
canvas.drawColor( mBgColor );
float fWidth = this.getWidth(); // View's width
float fHeight = this.getHeight(); // View's height
{
// draw the dock
float fTop = (2 * fHeight) / 3.0f;
Shader shader = new LinearGradient( 0, fTop, 0, fHeight, mDockTopGradientColor, mDockBottomGradientColor, TileMode.CLAMP );
Paint paint = new Paint();
paint.setShader(shader);
canvas.drawRect( new RectF( 0, fTop, fWidth, fHeight ), paint );
}
// moved to onLayout
// if ( mIconContainerView != null && mArrowBitmap != null ) {
// // set the icon container margin
// float fScale = fHeight / mArrowBitmap.getHeight();
// float fArrowWidth = mArrowBitmap.getWidth() * fScale;
// float fArrowHeight = mArrowBitmap.getHeight() * fScale;
// Log.d("DockView", "DockView.init: " + fArrowWidth + ", " + fArrowHeight );
// ((RelativeLayout.LayoutParams)mIconContainerView.getLayoutParams()).setMargins((int)fArrowWidth, 0, (int)fArrowWidth, 0);
// this.requestLayout();
// }
if ( mArrowBitmap != null ) {
// draw the arrow
// canvas.drawBitmap(mArrowBitmap, 0, 0, null);
float fScale = fHeight / mArrowBitmap.getHeight();
float fDrawnWidth = mArrowBitmap.getWidth() * fScale;
float fDrawnHeight = mArrowBitmap.getHeight() * fScale;
// float fLeft = fWidth - fDrawnWidth;
// float fTop = 0.0f;
// float fRight = fWidth;
// float fBottom = fDrawnHeight;
// Log.d("DockView", "DockView.onDraw: (" + fLeft + ", " + fTop + ", " + fRight + ", " + fBottom + ")");
canvas.drawBitmap(mArrowBitmap, null, new RectF(0, 0, fDrawnWidth, fDrawnHeight), null); // Left arrow
Log.d("DockView", "DockView.onDraw: (" + 0 + ", " + 0 + ", " + fDrawnWidth + ", " + fDrawnHeight + ")");
canvas.save();
canvas.scale(-1,1);
canvas.translate(-fWidth, 0);
// canvas.drawBitmap(mArrowBitmap, null, new RectF(fLeft, fTop, fRight, fBottom), null);
canvas.drawBitmap(mArrowBitmap, null, new RectF(0, 0, fDrawnWidth, fDrawnHeight), null); // Right arrow, flipped
canvas.restore();
}
}

How do I set opacity on a Bitmap?

I created a custom object that has a Bitmap field. I'm drawing several of these objects to the canvas of a View to mimic a scrolling horizontal image gallery.
When the user long presses one of the images, I want to change the opacity of the remaining Bitmaps to a specified percentage. This could give the impression that they've darkened for "edit mode", or it could mean they've returned to normal. (Please note that I don't want to permanently alter the Bitmaps. I want to be able to adjust their opacity on the fly.)
I pieced together the following code from various forums, and everything seems to be working except for the change in opacity. I've confirmed that my Bitmaps are mutable and have alpha every step of the way. What am I doing wrong?
Targeting Android 2.1, API Level 7
View (abridged for brevity):
public class CanvasStoryEdit2 extends View
{
public CanvasStoryEdit2(Context context, AttributeSet attrs) {
super(context, attrs);
for (int i = 0; i < getResources().getInteger(R.integer.maxAllowedSlides); i++)
{
ImageStoryEdit img = new ImageStoryEdit();
//test images
if (i == 0) { resource = R.drawable.a1; }
else if (i == 1) { resource = R.drawable.a2; }
else if (i == 2) { resource = R.drawable.a3; }
else if (i == 3) { resource = R.drawable.a4; }
else if (i == 4) { resource = R.drawable.a5; }
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resource);
Log.d("TEST", "[" + Integer.toString(i) + "] - config: " + bmp.getConfig().toString());
Log.d("TEST", "[" + Integer.toString(i) + "] - hasAlpha: " + Boolean.toString(bmp.hasAlpha()));
Log.d("TEST", "[" + Integer.toString(i) + "] - isMutable: " + Boolean.toString(bmp.isMutable()));
final int imgHeight = bmp.getHeight() / (bmp.getWidth() / imgWidth);
bmp = Bitmap.createScaledBitmap(bmp, imgWidth, imgHeight, false);
Log.d("TEST", "[" + Integer.toString(i) + "] - config: " + bmp.getConfig().toString());
Log.d("TEST", "[" + Integer.toString(i) + "] - hasAlpha: " + Boolean.toString(bmp.hasAlpha()));
Log.d("TEST", "[" + Integer.toString(i) + "] - isMutable: " + Boolean.toString(bmp.isMutable()));
int width = bmp.getWidth();
int height = bmp.getHeight();
int[] pixels = new int[width * height];
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
bmp.recycle();
bmp = null;
img.setBmp(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888));
img.getBmp().setPixels(pixels, 0, width, 0, 0, width, height);
pixels = null;
Log.d("TEST", "[" + Integer.toString(i) + "] - config: " + img.getBmp().getConfig().toString());
Log.d("TEST", "[" + Integer.toString(i) + "] - hasAlpha: " + Boolean.toString(img.getBmp().hasAlpha()));
Log.d("TEST", "[" + Integer.toString(i) + "] - isMutable: " + Boolean.toString(img.getBmp().isMutable()));
imageStoryEditList.add(img);
}
}
Call made on long press:
{
img.setOpacity(50);
invalidate();
}
ImageStoryEdit (also abridged):
public class ImageStoryEdit
{
private int opacity;
public Bitmap bmp;
public Bitmap getBmp() {
return bmp;
}
public void setBmp(Bitmap bmp)
{
this.bmp = bmp;
UpdateRectF();
}
public int getOpacity()
{
return opacity;
}
public void setOpacity(int percent)
{
//ADJUST FOR RANGE OF 0-100%
percent = percent < 0 ? 0 : percent;
percent = percent > 100 ? 100 : percent;
this.opacity = percent;
int opacity = (int) (this.opacity * 2.55);
Log.d("TEST", "OPACITY = " + Integer.toString(percent) + " : " + Integer.toString(opacity));
adjustOpacity(opacity);
}
private void adjustOpacity(int opacity)
{
Log.d("TEST", "OPACITY = " + Integer.toString(opacity));
Log.d("TEST", this.bmp.getConfig().toString());
Log.d("TEST", "hasAlpha: " + Boolean.toString(this.bmp.hasAlpha()));
Log.d("TEST", "isMutable: " + Boolean.toString(this.bmp.isMutable()));
Bitmap bmp2 = this.bmp.copy(Config.ARGB_8888, true);
Canvas canvas = new Canvas(bmp2);
Paint paint = new Paint();
paint.setAlpha(opacity);
canvas.drawBitmap(bmp2, 0, 0, paint);
this.bmp = bmp2;
Log.d("TEST", this.bmp.getConfig().toString());
Log.d("TEST", "hasAlpha: " + Boolean.toString(this.bmp.hasAlpha()));
Log.d("TEST", "isMutable: " + Boolean.toString(this.bmp.isMutable()));
Log.d("TEST", "DONE");
}
}
I think I figured this one out on my own, but I welcome any feedback if anyone can offer additional insight.
I was able to do what I wanted by using a BitmapDrawable. It's a wrapper for a Bitmap. Editing the Bitmap itself is, indeed, permanent. BitmapDrawables allow you to change certain parameters without directly affecting the underlying Bitmap.

Categories

Resources