How to set scrollbar thumb programmatically in Android? - android

I know listview can set scrollbar thumb in XML like this Android:scrollbarThumbVertical etc.
But I'm creating a listview instance in the java code and I maybe need to set different scrollbar thumb. Is there a method can set scrollbar thumb programmatically?

You can achieve that via reflection:
try
{
Field mScrollCacheField = View.class.getDeclaredField("mScrollCache");
mScrollCacheField.setAccessible(true);
Object mScrollCache = mScrollCacheField.get(listview);
Field scrollBarField = mScrollCache.getClass().getDeclaredField("scrollBar");
scrollBarField.setAccessible(true);
Object scrollBar = scrollBarField.get(mScrollCache);
Method method = scrollBar.getClass().getDeclaredMethod("setVerticalThumbDrawable", Drawable.class);
method.setAccessible(true);
method.invoke(scrollBar, getResources().getDrawable(R.drawable.scrollbar_style));
}
catch(Exception e)
{
e.printStackTrace();
}
The above code executes as:
listview.mScrollCache.scrollBar.setVerticalThumbDrawable(getResources().getDrawable(R.drawable.scrollbar_style));

I modified the answer to make it a method 100% programmatically
public static void ChangeColorScrollBar(View Scroll, int Color, Context cxt){
try
{
Field mScrollCacheField = View.class.getDeclaredField("mScrollCache");
mScrollCacheField.setAccessible(true);
Object mScrollCache = mScrollCacheField.get(Scroll);
Field scrollBarField = mScrollCache.getClass().getDeclaredField("scrollBar");
scrollBarField.setAccessible(true);
Object scrollBar = scrollBarField.get(mScrollCache);
Method method = scrollBar.getClass().getDeclaredMethod("setVerticalThumbDrawable", Drawable.class);
method.setAccessible(true);
Drawable[] layers = new Drawable[1];
ShapeDrawable sd1 = new ShapeDrawable(new RectShape());
sd1.getPaint().setColor(cxt.getResources().getColor(Color));
sd1.setIntrinsicWidth(Math.round(cxt.getResources().getDimension(R.dimen.dp_3)));
layers[0] = sd1;
method.invoke(scrollBar, layers);
}
catch(Exception e)
{
e.printStackTrace();
}
}

I didn't know the answer to this, but after a bit of digging around I don't think it's possible without a load of hassle.
This xml attribute is actually associated with a View, not a ListView - In the Android View source code, it seems that the only place that it is setting the vertical thumb drawable is the 'initializeScrollbars' method. Now this method isn't private, so we can extend any child of View and override this method, but the issue is that a crucial component needed to set the thumb drawable, the ScrollabilityCache, is private without any getter methods.
So without rewriting a lot of the code I don't think there's any easy way to do this - sorry!

Starting from Android Q (API level 29), you can use setVerticalScrollbarThumbDrawable() & setHorizontalScrollbarThumbDrawable():
scrollView.verticalScrollbarThumbDrawable =
ResourcesCompat.getDrawable(resources,
R.drawable.scrollview_thumb, null)

Related

How to change background color at image in widget android

I have a problem with change background color at image in widget.
Right now i'm using like this:
try {
Class c = Class.forName("android.widget.RemoteViews");
Method m = c.getMethod("setDrawableParameters", int.class, boolean.class, int.class, int.class, PorterDuff.Mode.class, int.class);
m.invoke(remoteViews, R.id.myImage, true, -1, Color.HSVToColor(color), PorterDuff.Mode.SRC_OVER, -1);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Everything works perfect, until i tested on android 9, here i receive an error:
java.lang.NoSuchMethodException: setDrawableParameters [int, boolean,
int, int, class android.graphics.PorterDuff$Mode, int]
Do you have any idea how can i make it work on android 9 also?
Thanks.
Another solution that worked for me is to have the image source in the xml layout android:src="#mipmap/myImage" and in code remoteViews.setInt(R.id.myImageView,"setColorFilter", myColor);
setDrawableParameters(..) is called setDrawableTint(..) in Android 9.0.
Here is the source code of the new method setDrawableTint(..) available in Android 9.0, I guess it does the same job as setDrawableParameters(..) in previous version of Android but you have to try it out in your project.
/**
* Equivalent to calling
* {#link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
* on the {#link Drawable} of a given view.
* <p>
* The operation will be performed on the {#link Drawable} returned by the
* target {#link View#getBackground()} by default. If targetBackground is false,
* we assume the target is an {#link ImageView} and try applying the operations
* to {#link ImageView#getDrawable()}.
* <p>
*/
private class SetDrawableTint extends Action {
SetDrawableTint(int id, boolean targetBackground,
int colorFilter, #NonNull PorterDuff.Mode mode) {
this.viewId = id;
this.targetBackground = targetBackground;
this.colorFilter = colorFilter;
this.filterMode = mode;
}
SetDrawableTint(Parcel parcel) {
viewId = parcel.readInt();
targetBackground = parcel.readInt() != 0;
colorFilter = parcel.readInt();
filterMode = PorterDuff.intToMode(parcel.readInt());
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(viewId);
dest.writeInt(targetBackground ? 1 : 0);
dest.writeInt(colorFilter);
dest.writeInt(PorterDuff.modeToInt(filterMode));
}
#Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
// Pick the correct drawable to modify for this view
Drawable targetDrawable = null;
if (targetBackground) {
targetDrawable = target.getBackground();
} else if (target instanceof ImageView) {
ImageView imageView = (ImageView) target;
targetDrawable = imageView.getDrawable();
}
if (targetDrawable != null) {
targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
}
}
#Override
public int getActionTag() {
return SET_DRAWABLE_TINT_TAG;
}
boolean targetBackground;
int colorFilter;
PorterDuff.Mode filterMode;
}
You could check how the new method works and what parameters needs and then have in your project something like:
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.P){
// Your actual implementation with "setDrawableParameters"
changeImageBackgroundColorBeforeAndroidPIE()
} else{
// Your new implementation with "setDrawableTint"
changeImageBackgroundColorAndroidPIEAndLater()
}
Another solution could be:
As I see the method setDrawableParameters(..) is hidden, so it is not meant to be used in this way, I guess it won't be the optimal solution to solve your problem. It looks like a hacky solution, but it can actually get worst once they change it or once it won't be supported in the way you thought it works.
Let's start from the beginning, you want to change the background color of an image in a widget, in a RemoteView more precisely, probably you could convert the drawable into a bitmap and then call RemoteViews.setImageBitmap()
Here is how to Convert a drawable into a bitmap
UPDATE: Another solution that the author just found was
Another solution that worked for me is to have the image source in the
xml layout android:src="#mipmap/myImage" and in code
remoteViews.setInt(R.id.myImageView,"setColorFilter", myColor);
Here the author answer: https://stackoverflow.com/a/55123126/3564632

how do I transform a string of url's into drawable?

I currently have an arraylist as follows:
private void loadImages() {
images = new ArrayList<>();
images.add(getResources().getDrawable(R.drawable.imag1));
images.add(getResources().getDrawable(R.drawable.imag2));
images.add(getResources().getDrawable(R.drawable.imag3));
}
I want to be able to convert a url into these drawables such that:
drawable1 = "http.someimage.com/image.png"
drawable2 = "http.someimage.com/newimage.png"
followed by
private void loadImages() {
images = new ArrayList<>();
images.add(getResources().getDrawable(drawable1));
images.add(getResources().getDrawable(drawable2));
...etc }
Is there any easy way to go around this? I definitely want to stick to drawables ,but I cant find any way to convert a url to drawable
Any ideas? Thanks!
If you have a URL of the picture you need to download it first.
You can't "convert" a URL into a drawable.
you need something like this:
URL url = new URL("http.someimage.com/image.png");
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
Then if you need to add the image into an ImageView object you can call the method .setImageBitmap(bmp).
Otherwise there are ways to extract a Drawable object from the Bitmap
you can check this previous answer. then once you have the drawable you can add it to your arraylist.
Hope I got your question right
P.S.: be sure not to do this on main thread since it is a network operation! use a thread or an asynctask

Get GridView horizontal spacing on Android 4.0

Is there any way to determine the horizontal spacing used by a GridView on Android 4.0.3 (API level 15)?
I have some working code targeting 4.1.x that uses GridView.GetRequestedHorizontalSpacing(), but this doesn't exist prior to API 16. Is there an equivalent to this, or a way to calculate it, on API level 15?
you can get it using Reflection API.
GridView gridView = (GridView) findViewById(R.id.gridView);
try {
Field field = gridView.getClass().getDeclaredField("mHorizontalSpacing");
field.setAccessible(true);
int hSpacing = field.getInt(gridView);
field = gridView.getClass().getDeclaredField("mRequestedHorizontalSpacing");
field.setAccessible(true);
int reqHSpacing = field.getInt(gridView);
} catch (Exception e) {
// TODO: handle exception
}

Load a set of images into a scrollview

Okay, so I have a JSON array of image URLs. I want to load them into a horizontal scrolling view. This is the way I've gone about it, but nothing gets displayed where the ScrollView should go:
public void run() {
JSONArray photosArray;
caption = new WebView(thisContext);
imageScroller = new HorizontalScrollView(thisContext);
imagesHolder = new LinearLayout(thisContext);
imagesHolder.setOrientation(LinearLayout.HORIZONTAL);
try {
photosArray = new JSONArray(postData.getString("photos"));
for(int i = 0; i < photosArray.length(); i++) {
WebView iV = new WebView(thisContext);
JSONObject thisPhoto = photosArray.getJSONObject(i);
JSONArray sizesArray = new JSONArray(thisPhoto.getString("alt_sizes"));
JSONObject largest = sizesArray.getJSONObject(0);
iV.loadData("<img src=\""+largest.getString("url")+"\" />", "text/html", null);
imagesHolder.addView(iV);
}
imageScroller.addView(imagesHolder);
myPostHolder.addView(imageScroller);
caption.loadData(postData.getString("caption"),"text/html",null);
myPostHolder.addView(caption);
} catch (Exception e) {
e.printStackTrace();
}
}
Note that this is done in a runnable class. Thanks a lot
There are two rules in the Android single thread model.
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread
So I don't think you can add views to a viewgroup outside of the main thread. Please see: http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html for a fuller explanation.
Also, you need to make sure you are actually setting the contentview and you may need to call invalidate() on the viewgroup to cause a redraw.
And, you probably should be using a GridView..can't see why you'd use a webview for this.

Results of AsyncTask into local variable

I'm working on implementing a website's API in an Android app. There's a part of this API which gives me an array, which contains an array of image URLs. Now, I can iterate through the arrays just fine, and I have written an AsyncTask that transforms image URLs into drawables. But I don't have a good way to get those drawables into the ImageView (which I am creating programatically in the loop):
void createPhotoPost(JSONObject postData, LinearLayout myPostHolder) {
try {
ImageView myImage = new ImageView(myContext);
JSONArray photosArray = new JSONArray(postData.getString("photos"));
for(int i = 0; i < photosArray.length(); i++) {
JSONObject thisPhoto = photosArray.getJSONObject(i);
JSONArray sizesArray = new JSONArray(thisPhoto.getString("alt_sizes"));
JSONObject largest = sizesArray.getJSONObject(0);
new GetPhotoSet(this).execute(largest.getString("url"));
}
myPostHolder.addView(myImage);
} catch (Exception e) {
e.printStackTrace();
}
}
The function I posted above is called from within another loop. As you can see in my code above, the ImageView myImage needs to eventually contain the drawable that is returned by the execute call of GetPhotoSet.execute. Any ideas? Thanks!
Additional details: Since the method where the data is returned by the AsyncTask is outside of the loop where I declared the ImageView myImage, it doesn't know that myImage exists, and it can't reference it (and there are presumably a handful of myImages by this point, from different iterations of the loop)
Nick, that's what onPostExecute() is for. Create your ImageView in onPostExecute() and add it to your Activity from there. If you are looping over a series of images, you can also use publishProgress() to do this. In your example you should move the entire loop to your task's doInBackground().

Categories

Resources