Ok, first off all, I have not yet converted to using RecylerView; if for whatever reason doing that answers this question, by all means let me know.
In the meantime, I am trying to add a splash of different colors into a ListView using Palette.
Picture this: In Twitter, you have a timeline, each row has a user profile image. I am doing the equivalent of making the background of each row based on the user profile image.
I am also using Picasso to load the images.
In my Adapter, I do this:
try {
Picasso.with(context).load(url).placeholder(R.drawable.usericon).transform(new CircularTransform())
.into(holder.i1);
holder.i1.buildDrawingCache();
Bitmap b = holder.i1.getDrawingCache();
final ViewHolder finalHolder = holder;
Palette.generateAsync(b, new Palette.PaletteAsyncListener() {
#Override
public void onGenerated(Palette palette) {
Palette.Swatch vibrant = palette.getVibrantSwatch();
if (vibrant != null) {
finalHolder.r1.setBackgroundColor(
vibrant.getRgb());
finalHolder.tv3.setTextColor(
vibrant.getTitleTextColor());
finalHolder.l1.setBackgroundColor(
vibrant.getRgb());
finalHolder.tv8.setTextColor(
vibrant.getTitleTextColor());
}
}
});
} catch (Exception e) {
}
this does work; but the problem is when the list is first loaded, the rows are the default colors. I can sit there without scrolling for 5 seconds or 5 minutes and it will not change until I start scrolling. Then the rows below ARE the new color based on the Swatch. If I go back up to the top, those rows are now colored as well.
So my question: Is what I am doing too ambitious for this api? if not, is there a better way to speed things up?
Related
I am trying to create a tile on the Microsoft Band 2 using Android Studio. I was wondering if it is possible to add images to a button like I would be able to on an android phone. My other question is about checkboxes. Are there checkboxes on the band? If not is there another way to get similar functionality? I need users to be able to click multiple things for a single question. Any help would be greatly appreciated.
For the checkboxes I would do a layout where you have a small text button next to a larger text button within a layout. When the large text button gets clicked, call an update function from your receiver that changes the text of the smaller button (possibly an asterisk or some other character that looks like a bullet point and it seems to appear and disappear). For example, your update function could look like this (slight modification from the example tile code given in the SDK):
private final int bulletTextId = 12;
private final int textButtonId = 21;
private boolean isActiveBullet = false;
private void onButtonClicked(int clickedID) {
switch (clickedID) {
case textButtonId:
String text = "";
isActiveBullet = !isActiveBullet;
if (isActiveBullet) text = "*";
try {
client.getTileManager().setPages(tileId,
new PageData(pageId1, 0)
.update(new TextBlockData(bulletTextId, text))
.update(new TextButtonData(textButtonId, "Text Button")));
} catch (BandIOException e) {e.printStackTrace();}
break;
default:
Log.e("", "Unknown button press received");
}
}
For multiple buttons you might need a map of button to boolean and switch the corresponding ones. If you can't figure that out, comment and I'll follow up.
Originally I was thinking it would make sense to change the background color, but that doesn't seem to be supported by the sdk.
As for using an image for the background, I don't think that is currently supported, just from looking at the function definitions in the sdk source code, but I would actually love to know that for sure as well.
Edit: I found this shortly after posting. It appears you can use bitmaps as masks, but I am not sure how to do that. Hopefully someone will come along and tell us because I would like to know too :)
"8.5.1
Icons Used as
FilledButton
Masks
By defining an Icon bitmap that acts as a mask and
then
superimposing that Icon over a
FilledButton
(see
Negative
Margins
)
, you can create the effect of the Icon image becoming visible when the button is pressed. That is, the
Icon bitmap
is defined to
have transparent pixels for the desired image, and opaque pixels els
e
where
. When the
user presses the
FilledButton
, the
FilledButton
color changes
but
shows through
only
the
transparent portions of
the Icon bitmap. "
And here is other relevant code if you want it:
private PageLayout createButtonLayout() {
return new PageLayout(
new ScrollFlowPanel(15, 0, 260, 105, FlowPanelOrientation.HORIZONTAL)
.addElements(new TextBlock(0, 0, 20, 45, TextBlockFont.MEDIUM)
.setMargins(5, 0, 0, 0)
.setId(bulletTextId))
.addElements(new TextButton(0, 0, 190, 45).setMargins(5, 0, 0, 0)
.setId(textButtonId).setPressedColor(Color.BLUE))
);
}
private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == TileEvent.ACTION_TILE_OPENED) {
} else if (intent.getAction() == TileEvent.ACTION_TILE_CLOSED) {
}
/* ***** THIS IS THE ONLY EVENT WE ACTUALLY CARE ABOUT ***** */
else if (intent.getAction() == TileEvent.ACTION_TILE_BUTTON_PRESSED) {
TileButtonEvent buttonData = intent.getParcelableExtra(TileEvent.TILE_EVENT_DATA);
appendToUI("button is " + isActiveBullet + " ");
onButtonClicked(buttonData.getElementID());
}
}
};
I have RecyclerView where every list item has an ImageButton, thee image of which I define in the adapter's onBindViewHolder():
int myVote = getMyVote();
if (myVote != 0) {
Log.d("dbg", myVote + "");
holder.ratingButton.setImageResource(R.drawable.ic_star_grey600_36dp);
}
So ratingButton is a star in the right bottom corner of the list item layout. Its shape is filled with gray color (and accordingly, a log record is pushed) if the condition (myVote != 0) is satisfied.
The problem is that when I scroll the list down I can watch other stars became filled even though I can see the only one record in the log window (for the correct list item). Moreover, this list items with incorrectly changed buttons repeat every 5 rows, and that's what's confusing me. If I changemListView.setItemViewCacheSize(0); the repeat period changes to 3, so we can assume it somehow connected with the RecyclerView's caching and recycling mechanism.
Please, help me to work the problem out. Thanks!
Don't forget to implement
public long getItemId(int position) {}
otherwise, you'll see repeated items in RecyclerView.
Try to change your code to:
if (myVote != 0) {
Log.d("dbg", myVote + "");
holder.ratingButton.setImageResource(R.drawable.ic_star_grey600_36dp);
} else {
holder.ratingButton.setImageResource(int another resource);
}
}
You may also have to write else part of main condition with some another resource like:
if (myVote != 0) {
Log.d("dbg", myVote + "");
holder.ratingButton.setImageResource(R.drawable.ic_star_grey600_36dp);
} else {
holder.ratingButton.setImageResource(int another_resource);
}
It is worked for me.
Hello so im working on a Music Player for android and i wanted help on making the background color the same as the album art of the playing song kind of like Sony Walkman.So can somebody please show how can it be done or at least get me on track on how it should be done.
I started Android recently so go easy on Me,and sorry for bad english
You can use the v7 palette support library. It includes the Palette class, which lets you extract prominent colors from an image.
https://developer.android.com/reference/android/support/v7/graphics/Palette.html
Example
build.gradle
compile 'com.android.support:palette-v7:23.4.0'
Activity or fragment
public void updatePlayerBar(Bitmap bitmap) {
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
public void onGenerated(Palette palette) {
Palette.Swatch swatch = palette.getVibrantSwatch();
if (swatch == null) swatch = palette.getMutedSwatch(); // Sometimes vibrant swatch is not available
if (swatch != null) {
// Set the background color of the player bar based on the swatch color
mContent.setBackgroundColor(swatch.getRgb());
// Update the track's title with the proper title text color
mTitle.setTextColor(swatch.getTitleTextColor());
// Update the artist name with the proper body text color
mArtist.setTextColor(swatch.getBodyTextColor());
}
}
});
}
I'm building an interface similar to the Google Hangouts chat interface. New messages are added to the bottom of the list. Scrolling up to the top of the list will trigger a load of previous message history. When the history comes in from the network, those messages are added to the top of the list and should not trigger any kind of scroll from the position the user had stopped when the load was triggered. In other words, a "loading indicator" is shown at the top of the list:
Which is then replaced in-situ with any loaded history.
I have all of this working... except one thing that I've had to resort to reflection to accomplish. There are plenty of questions and answers involving merely saving and restoring a scroll position when adding items to the adapter attached to a ListView. My problem is that when I do something like the following (simplified but should be self-explanatory):
public void addNewItems(List<Item> items) {
final int positionToSave = listView.getFirstVisiblePosition();
adapter.addAll(items);
listView.post(new Runnable() {
#Override
public void run() {
listView.setSelection(positionToSave);
}
});
}
Then what the user will see is a quick flash to the top of the ListView, then a quick flash back to the right location. The problem is fairly obvious and discovered by many people: setSelection() is unhappy until after notifyDataSetChanged() and a redraw of ListView. So we have to post() to the view to give it a chance to draw. But that looks terrible.
I've "fixed" it by using reflection. I hate it. At its core, what I want to accomplish is reset the first position of the ListView without going through the rigamarole of the draw cycle until after I've set the position. To do that, there's a helpful field of ListView: mFirstPosition. By gawd, that's exactly what I need to adjust! Unfortunately, it's package-private. Also unfortunately, there doesn't appear to be any way to set it programmatically or influence it in any way that doesn't involve an invalidate cycle... yielding the ugly behavior.
So, reflection with a fallback on failure:
try {
Field field = AdapterView.class.getDeclaredField("mFirstPosition");
field.setAccessible(true);
field.setInt(listView, positionToSave);
}
catch (Exception e) { // CATCH ALL THE EXCEPTIONS </meme>
e.printStackTrace();
listView.post(new Runnable() {
#Override
public void run() {
listView.setSelection(positionToSave);
}
});
}
}
Does it work? Yes. Is it hideous? Yes. Will it work in the future? Who knows? Is there a better way? That's my question.
How do I accomplish this without reflection?
An answer might be "write your own ListView that can handle this." I'll merely ask whether you've seen the code for ListView.
EDIT: Working solution with no reflection based on Luksprog's comment/answer.
Luksprog recommended an OnPreDrawListener(). Fascinating! I've messed with ViewTreeObservers before, but never one of these. After some messing around, the following type of thing appears to work quite perfectly.
public void addNewItems(List<Item> items) {
final int positionToSave = listView.getFirstVisiblePosition();
adapter.addAll(items);
listView.post(new Runnable() {
#Override
public void run() {
listView.setSelection(positionToSave);
}
});
listView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if(listView.getFirstVisiblePosition() == positionToSave) {
listView.getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
else {
return false;
}
}
});
}
Very cool.
As I said in my comment, a OnPreDrawlistener could be another option to solve the problem. The idea of using the listener is to skip showing the ListView between the two states(after adding the data and after setting the selection to the right position). In the OnPreDrawListener(set with listViewReference.getViewTreeObserver().addOnPreDrawListener(listener);) you'll check the current visible position of the ListView and test it against the position which the ListView should show. If those don't match then make the listener's method return false to skip the frame and set the selection on the ListView to the right position. Setting the proper selection will trigger the draw listener again, this time the positions will match, in which case you'd unregister the OnPreDrawlistener and return true.
I was breaking up my head until I found a solution similar to this.
Before adding a set of items you have to save top distance of the firstVisible item and after adding the items do setSelectionFromTop().
Here is the code:
// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
// for (Item item : items){
mListAdapter.add(item);
}
// restore index and top position
mList.setSelectionFromTop(index, top);
It works without any jump for me with a list of about 500 items :)
I took this code from this SO post: Retaining position in ListView after calling notifyDataSetChanged
The code suggested by the question author works, but it's dangerous.
For instance, this condition:
listView.getFirstVisiblePosition() == positionToSave
may always be true if no items were changed.
I had some problems with this aproach in a situation where any number of elements were added both above and below the current element. So I came up with a sligtly improved version:
/* This listener will block any listView redraws utils unlock() is called */
private class ListViewPredrawListener implements OnPreDrawListener {
private View view;
private boolean locked;
private ListViewPredrawListener(View view) {
this.view = view;
}
public void lock() {
if (!locked) {
locked = true;
view.getViewTreeObserver().addOnPreDrawListener(this);
}
}
public void unlock() {
if (locked) {
locked = false;
view.getViewTreeObserver().removeOnPreDrawListener(this);
}
}
#Override
public boolean onPreDraw() {
return false;
}
}
/* Method inside our BaseAdapter */
private updateList(List<Item> newItems) {
int pos = listView.getFirstVisiblePosition();
View cell = listView.getChildAt(pos);
String savedId = adapter.getItemId(pos); // item the user is currently looking at
savedPositionOffset = cell == null ? 0 : cell.getTop(); // current item top offset
// Now we block listView drawing until after setSelectionFromTop() is called
final ListViewPredrawListener predrawListener = new ListViewPredrawListener(listView);
predrawListener.lock();
// We have no idea what changed between items and newItems, the only assumption
// that we make is that item with savedId is still in the newItems list
items = newItems;
notifyDataSetChanged();
// or for ArrayAdapter:
//clear();
//addAll(newItems);
listView.post(new Runnable() {
#Override
public void run() {
// Now we can finally unlock listView drawing
// Note that this code will always be executed
predrawListener.unlock();
int newPosition = ...; // Calculate new position based on the savedId
listView.setSelectionFromTop(newPosition, savedPositionOffset);
}
});
}
my problem is, I have custom listView filled from Runnable returnRes. It fills particular data in layout I have named as lay (R.id.layoutList). My aim is to have different colour for each lay in my listView, I want to switch colours between each. 1st is dark blue, second light blue, thir dark blue and so on... This code is doing well, but with no result, my custom listView is still black, when I change it in XML, it is changing, but not when it is set from Java. Any ideas?
Thanks
private Runnable returnRes = new Runnable() {
#Override
public void run() {
if(myTasks != null && myTasks.size() > 0){
TasksAdapter.notifyDataSetChanged();
LinearLayout lay=(LinearLayout)findViewById(R.id.layoutList);
for(int i=0;i<myTasks.size();i++){
TasksAdapter.add(myTasks.get(i));
if(i>0){
if(i%2==0){
lay.setBackgroundColor(R.color.background);
}
}else{
if(i>0){
lay.setBackgroundColor(R.color.lightBlue);
}
}
}
}
ProgressDialog.dismiss();
TasksAdapter.notifyDataSetChanged();
}
};
Try googling. getResources().R.color.lightBlue is not the actual color, it's the id of
the color resource (which is an integer code for the color). It will work fine if you use it
in an API which expects ids of resources, but setBackgroundColor
actually needs the code of the color.
colors and ids are both just coded as int when you come down to it, so it's
really easy to confuse one for the other.
yourlayout.setBackgroundDrawable(getResources().getDrawable(R.drawable.yourbackgroundimage))