Java tutorial
/* * Copyright 2015 Ayuget * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ayuget.redface.ui.activity; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v4.view.MotionEventCompat; import android.support.v7.app.ActionBar; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Spinner; import android.widget.TextView; import com.ayuget.redface.R; import com.ayuget.redface.account.UserManager; import com.ayuget.redface.data.DataService; import com.ayuget.redface.data.api.MDEndpoints; import com.ayuget.redface.data.api.MDService; import com.ayuget.redface.data.api.model.Response; import com.ayuget.redface.data.api.model.Smiley; import com.ayuget.redface.data.api.model.Topic; import com.ayuget.redface.data.api.model.User; import com.ayuget.redface.data.rx.EndlessObserver; import com.ayuget.redface.data.rx.SubscriptionHandler; import com.ayuget.redface.data.state.ResponseStore; import com.ayuget.redface.network.HTTPClientProvider; import com.ayuget.redface.ui.UIConstants; import com.ayuget.redface.ui.event.SmileySelectedEvent; import com.ayuget.redface.ui.misc.BindableAdapter; import com.ayuget.redface.ui.misc.UiUtils; import com.ayuget.redface.ui.template.SmileysTemplate; import com.ayuget.redface.ui.view.SmileySelectorView; import com.ayuget.redface.util.UserUtils; import com.google.common.base.Optional; import com.squareup.otto.Subscribe; import com.squareup.picasso.Picasso; import java.util.Arrays; import java.util.List; import javax.inject.Inject; import butterknife.InjectView; import butterknife.OnClick; public class ReplyActivity extends BaseActivity implements Toolbar.OnMenuItemClickListener { private static final String LOG_TAG = ReplyActivity.class.getSimpleName(); private static final String ARG_TOPIC = "topic"; /** * @todo fix this, ugly... */ private static final List<Smiley> DEFAULT_SMILEYS = Arrays.asList( Smiley.make(":O", "http://forum-images.hardware.fr/icones/redface.gif"), Smiley.make(":)", "http://forum-images.hardware.fr/icones/smile.gif"), Smiley.make(":(", "http://forum-images.hardware.fr/icones/frown.gif"), Smiley.make(":D", "http://forum-images.hardware.fr/icones/biggrin.gif"), Smiley.make(";)", "http://forum-images.hardware.fr/icones/wink.gif"), Smiley.make(":ouch:", "http://forum-images.hardware.fr/icones/smilies/ouch.gif"), Smiley.make(":??:", "http://forum-images.hardware.fr/icones/confused.gif"), Smiley.make(":p", "http://forum-images.hardware.fr/icones/tongue.gif"), Smiley.make(":pfff:", "http://forum-images.hardware.fr/icones/smilies/pfff.gif"), Smiley.make(":ange:", "http://forum-images.hardware.fr/icones/smilies/ange.gif"), Smiley.make(":non:", "http://forum-images.hardware.fr/icones/smilies/non.gif"), Smiley.make(":bounce:", "http://forum-images.hardware.fr/icones/smilies/bounce.gif"), Smiley.make(":fou:", "http://forum-images.hardware.fr/icones/smilies/fou.gif"), Smiley.make(":jap:", "http://forum-images.hardware.fr/icones/smilies/jap.gif"), Smiley.make(":lol:", "http://forum-images.hardware.fr/icones/smilies/lol.gif"), Smiley.make(":wahoo:", "http://forum-images.hardware.fr/icones/smilies/wahoo.gif"), Smiley.make(":kaola:", "http://forum-images.hardware.fr/icones/smilies/kaola.gif"), Smiley.make(":love:", "http://forum-images.hardware.fr/icones/smilies/love.gif"), Smiley.make(":heink:", "http://forum-images.hardware.fr/icones/smilies/heink.gif"), Smiley.make(":cry:", "http://forum-images.hardware.fr/icones/smilies/cry.gif"), Smiley.make(":whistle:", "http://forum-images.hardware.fr/icones/smilies/whistle.gif"), Smiley.make(":sol:", "http://forum-images.hardware.fr/icones/smilies/sol.gif"), Smiley.make(":pt1cable:", "http://forum-images.hardware.fr/icones/smilies/pt1cable.gif"), Smiley.make(":sleep:", "http://forum-images.hardware.fr/icones/smilies/sleep.gif"), Smiley.make(":sweat:", "http://forum-images.hardware.fr/icones/smilies/sweat.gif"), Smiley.make(":hello:", "http://forum-images.hardware.fr/icones/smilies/hello.gif"), Smiley.make(":na:", "http://forum-images.hardware.fr/icones/smilies/na.gif"), Smiley.make(":sarcastic:", "http://forum-images.hardware.fr/icones/smilies/sarcastic.gif")); /** * The active pointer is the one currently use to move the smiley view */ private int activePointerId = UIConstants.INVALID_POINTER_ID; /** * Top offset (margin) in pixels for the smiley selector. Marks its default position on the * y-axis. */ private int smileySelectorTopOffset; /** * Reply window max height in pixels */ private int replyWindowMaxHeight; private int screenHeight; private float lastTouchY; private boolean isUpwardMovement; private int toolbarHeight; private boolean smileysToolbarAnimationInProgress; /** * Main reply window (with user picker, toolbars, ...) */ @InjectView(R.id.main_reply_frame) RelativeLayout mainReplyFrame; /** * Primary dialog toolbar, with user selection and send button */ @InjectView(R.id.toolbar_reply_actions) Toolbar actionsToolbar; /** * Secondary action toolbar (bold, italic, links, ...) */ @InjectView(R.id.toolbar_reply_extra) Toolbar extrasToolbar; /** * Finger draggable view to select smileys */ @InjectView(R.id.smiley_selector_view) View smileysSelector; /** * Toolbar to switch between popular / recent / favorite smileys */ @InjectView(R.id.smileys_toolbar) Toolbar smileysToolbar; /** * Reply text box */ @InjectView(R.id.reply_text) EditText replyEditText; /** * Smiley list loading indicator */ @InjectView(R.id.loading_indicator) View smileysLoadingIndicator; /** * Smiley list */ @InjectView(R.id.smileyList) SmileySelectorView smileyList; /** * Root ViewGroup for the reply window */ @InjectView(R.id.reply_window_root) FrameLayout replyWindowRoot; /** * Smileys search box */ @InjectView(R.id.smileys_search) SearchView smileysSearch; @InjectView(R.id.sending_message_spinner) View sendingMessageSpinner; @Inject UserManager userManager; @Inject SmileysTemplate smileysTemplate; @Inject MDEndpoints mdEndpoints; @Inject DataService dataService; @Inject HTTPClientProvider httpClientProvider; @Inject MDService mdService; @Inject ResponseStore responseStore; private Topic currentTopic; private String initialReplyContent; private SubscriptionHandler<User, Response> replySubscriptionHandler = new SubscriptionHandler<>(); private boolean replyIsSuccessful = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutResource()); Intent intent = getIntent(); if (intent != null) { currentTopic = intent.getParcelableExtra(ARG_TOPIC); initialReplyContent = intent.getStringExtra(UIConstants.ARG_REPLY_CONTENT); if (initialReplyContent != null) { replyEditText.setText(initialReplyContent); replyEditText.setSelection(replyEditText.getText().length()); } } DisplayMetrics metrics = getResources().getDisplayMetrics(); screenHeight = metrics.heightPixels; smileySelectorTopOffset = (int) (metrics.heightPixels * 0.75); replyWindowMaxHeight = (int) (metrics.heightPixels * 0.70); toolbarHeight = getToolbarHeight(); setupSmileySelector(); actionsToolbar.inflateMenu(R.menu.menu_reply); actionsToolbar.setOnMenuItemClickListener(this); setupUserSwitcher(getLayoutInflater(), userManager.getRealUsers()); // Dirty hack to set a maximum height on the reply window frame. Should leave enough room for the smiley // picker when the soft keyboard is hidden, and hide the smiley picker otherwise. Activity is resized thanks // to the adjustResize windowSoftInputMode set in the manifest, and the extra bottom toolbar stays visible // and usable. // // As any hack, this method is probably very buggy... replyWindowRoot.getViewTreeObserver() .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect r = new Rect(); replyWindowRoot.getWindowVisibleDisplayFrame(r); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mainReplyFrame.getLayoutParams(); boolean keyboardIsOpen = r.height() < replyWindowMaxHeight; if (keyboardIsOpen) { if (lp.height != ViewGroup.LayoutParams.MATCH_PARENT) { lp.height = ViewGroup.LayoutParams.MATCH_PARENT; mainReplyFrame.setLayoutParams(lp); } } else { if (lp.height != replyWindowMaxHeight) { lp.height = replyWindowMaxHeight; mainReplyFrame.setLayoutParams(lp); } } } }); styleToolbarButtons(extrasToolbar); styleToolbarMenu(actionsToolbar); smileysSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { if (s.trim().length() > 0) { smileyList.reset(); smileysLoadingIndicator.setVisibility(View.VISIBLE); subscribe(dataService.searchForSmileys(s.trim(), new EndlessObserver<List<Smiley>>() { @Override public void onNext(List<Smiley> smileys) { smileysLoadingIndicator.setVisibility(View.GONE); smileyList.setSmileys(smileys); } })); } return false; } @Override public boolean onQueryTextChange(String s) { return false; } }); // Load default smileys loadDefaultSmileys(); } protected int getLayoutResource() { return R.layout.dialog_reply; } @Override protected void onPause() { super.onPause(); if (!isReplySuccessful() && replyEditText != null && currentTopic != null) { String actualReply = replyEditText.getText().toString(); boolean hasResponse = actualReply.length() > 0; boolean textWasModified = (initialReplyContent == null) || !initialReplyContent.equals(actualReply); if (hasResponse && textWasModified) { responseStore.storeResponse(userManager.getActiveUser(), currentTopic, replyEditText.getText().toString()); } else { responseStore.removeResponse(userManager.getActiveUser(), currentTopic); } } } @Override protected void onResume() { super.onResume(); if (currentTopic != null) { String storedResponse = responseStore.getResponse(userManager.getActiveUser(), currentTopic); if (storedResponse != null && replyEditText.getText().length() == 0) { replyEditText.setText(storedResponse); replyEditText.setSelection(replyEditText.getText().length()); } } } /** * Loads default smileys in the smiley selector */ @OnClick(R.id.default_smileys) protected void loadDefaultSmileys() { smileyList.setSmileys(DEFAULT_SMILEYS); } /** * Loads recently used smileys in the smiley selector */ @OnClick(R.id.recent_smileys) protected void loadRecentSmileys() { smileyList.reset(); smileysLoadingIndicator.setVisibility(View.VISIBLE); subscribe(dataService.getRecentlyUsedSmileys(userManager.getActiveUser(), new EndlessObserver<List<Smiley>>() { @Override public void onNext(List<Smiley> smileys) { smileysLoadingIndicator.setVisibility(View.GONE); smileyList.setSmileys(smileys); } })); } /** * Loads popular smileys in the smiley selector */ @OnClick(R.id.popular_smileys) protected void loadPopularSmileys() { smileyList.reset(); smileysLoadingIndicator.setVisibility(View.VISIBLE); subscribe(dataService.getPopularSmileys(new EndlessObserver<List<Smiley>>() { @Override public void onNext(List<Smiley> smileys) { smileysLoadingIndicator.setVisibility(View.GONE); smileyList.setSmileys(smileys); } })); } protected void showSendingMessageSpinner() { sendingMessageSpinner.setVisibility(View.VISIBLE); actionsToolbar.getMenu().getItem(0).setVisible(false); } protected void hideSendingMessageSpinner() { sendingMessageSpinner.setVisibility(View.GONE); actionsToolbar.getMenu().getItem(0).setVisible(true); } /** * Initializes both the smiley selector */ protected void setupSmileySelector() { smileyList.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { final int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_DOWN: { final int pointerIndex = MotionEventCompat.getActionIndex(event); lastTouchY = MotionEventCompat.getY(event, pointerIndex); activePointerId = MotionEventCompat.getPointerId(event, 0); break; } case MotionEvent.ACTION_MOVE: { if (smileyList.getScrollY() == 0) { final int pointerIndex = MotionEventCompat.findPointerIndex(event, activePointerId); if (pointerIndex != -1) { final float y = MotionEventCompat.getY(event, pointerIndex); // Distance float dy = y - lastTouchY; isUpwardMovement = dy < 0; float targetY = smileysSelector.getY() + dy; if (targetY < toolbarHeight) { float difference = toolbarHeight - targetY; dy += difference; } else if (targetY > smileySelectorTopOffset) { float difference = targetY - smileySelectorTopOffset; dy -= difference; } smileysSelector.setY(smileysSelector.getY() + dy); // Show or hide the smileys toolbar based on current position if (isUpwardMovement && smileysSelector.getY() < replyWindowMaxHeight) { showSmileysToolbar(); } else { hideSmileysToolbar(); } } break; } } case MotionEvent.ACTION_UP: { int upAnimationThreshold = replyWindowMaxHeight - toolbarHeight; float yTranslation; ViewPropertyAnimator viewPropertyAnimator = smileysSelector.animate(); if (isUpwardMovement && smileysSelector.getY() == upAnimationThreshold) { // Do not move in that case yTranslation = 0; } else if (isUpwardMovement && smileysSelector.getY() < upAnimationThreshold) { // Moving too far, let's avoid this yTranslation = -(smileysSelector.getY() - toolbarHeight); } else { // Replace the smiley selector at its original position yTranslation = smileySelectorTopOffset - smileysSelector.getY(); } if (yTranslation != 0) { viewPropertyAnimator.translationYBy(yTranslation).setDuration(150).start(); } break; } } boolean touchConsumed; if (smileysSelector.getY() != smileySelectorTopOffset) { touchConsumed = (smileysSelector.getY() != toolbarHeight); } else { touchConsumed = false; } return touchConsumed; } }); } /** * Hides the soft keyboard */ public void hideSoftKeyboard() { if (getCurrentFocus() != null) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); } } @OnClick(R.id.show_smileys_picker) protected void showSmileyPicker() { float neededTranslation = -(smileysSelector.getY() - toolbarHeight); ViewPropertyAnimator viewPropertyAnimator = smileysSelector.animate(); viewPropertyAnimator.translationYBy(neededTranslation).setDuration(150).start(); showSmileysToolbar(); hideSoftKeyboard(); } /** * Smoothly hides the smileys toolbar */ protected void hideSmileysToolbar() { smileysToolbar.animate().translationY(-toolbarHeight) .setInterpolator(new AccelerateDecelerateInterpolator()).start(); } /** * Smoothly shows the smiley toolbar */ protected void showSmileysToolbar() { smileysToolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator()).start(); } @Override protected void initializeTheme() { setTheme(themeManager.getReplyWindowStyle()); } protected boolean canSwitchUser() { return false; } protected void setupUserSwitcher(LayoutInflater inflater, List<User> users) { if (users.size() == 0) { Log.e(LOG_TAG, "Empty user list"); } else if (users.size() == 1 || !canSwitchUser()) { View userView = setupUserView(inflater, canSwitchUser() ? users.get(0) : userManager.getActiveUser()); actionsToolbar.addView(userView); } else { // Setup spinner for user selection Log.d(LOG_TAG, String.format("Initializing spinner for '%d' users", users.size())); View spinnerContainer = inflater.inflate(R.layout.reply_user_spinner, actionsToolbar, false); ActionBar.LayoutParams lp = new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); actionsToolbar.addView(spinnerContainer, 0, lp); Spinner spinner = (Spinner) spinnerContainer.findViewById(R.id.reply_user_spinner); final UserAdapter userAdapter = new UserAdapter(this, users); spinner.setAdapter(userAdapter); spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { User user = userAdapter.getItem(position); userManager.setActiveUser(user); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); } } protected View setupUserView(LayoutInflater inflater, User user) { View userView = inflater.inflate(R.layout.dialog_reply_spinner_item, actionsToolbar, false); ImageView avatarView = (ImageView) userView.findViewById(R.id.user_avatar); TextView usernameView = (TextView) userView.findViewById(R.id.user_username); avatarView.setImageResource(R.drawable.profile_background_red); usernameView.setText(user.getUsername()); if (!user.isGuest()) { loadUserAvatarInto(user, avatarView); } return userView; } @Subscribe public void smileySelected(SmileySelectedEvent event) { insertText(String.format(" %s ", event.getSmileyCode())); replaceSmileySelector(); hideSmileysToolbar(); } protected void loadUserAvatarInto(User user, ImageView imageView) { if (user.hasAvatar()) { Picasso.with(this).load(user.getProfile().getAvatarUrl()).into(imageView); } } /** * Returns toolbar height, in px */ protected int getToolbarHeight() { TypedValue tv = new TypedValue(); getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); return getResources().getDimensionPixelSize(tv.resourceId); } /** * Inserts a text at current caret position * @param text text to insert */ protected void insertText(String text) { int selectionStart = replyEditText.getSelectionStart(); int selectionEnd = replyEditText.getSelectionEnd(); if (selectionStart != -1 && selectionEnd != -1) { replyEditText.getText().replace(selectionStart, selectionEnd, text); } else if (selectionStart != -1) { replyEditText.getText().insert(selectionStart, text); } } protected void insertSmileyOrTag(boolean isSmiley, String tag) { int selectionStart = replyEditText.getSelectionStart(); int selectionEnd = replyEditText.getSelectionEnd(); String selectedText = (selectionEnd == -1 || selectionEnd <= selectionStart) ? "" : replyEditText.getText().toString().substring(selectionStart, selectionEnd); String tagOpen = isSmiley ? "[:" : String.format("[%s]", tag); String tagClose = isSmiley ? "]" : String.format("[/%s]", tag); insertText(tagOpen + selectedText + tagClose); replyEditText.setSelection(selectionStart + tagOpen.length() + selectedText.length()); } @Override protected void onStart() { super.onStart(); // Move the smiley picker to the bottom and resize it properly smileysSelector.setMinimumHeight(screenHeight - getToolbarHeight()); smileysSelector.setY(smileySelectorTopOffset); // Hide the smileys toolbar smileysToolbar.setTranslationY(-getToolbarHeight()); } @Override public boolean onMenuItemClick(MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.action_reply: // Disable button to prevent user from clicking the button twice and posting the // same reply multiple times. This could happen often because there is a small delay // between the click and the activity being closed. menuItem.setEnabled(false); showSendingMessageSpinner(); postReply(); break; } return false; } @OnClick({ R.id.insert_manual_smiley_button, R.id.make_text_bold_button, R.id.make_text_italic_button, R.id.insert_quote_button, R.id.insert_link_button, R.id.insert_spoiler_button, R.id.insert_image_button }) public void onExtraToolbarButtonClicked(ImageButton button) { Log.d(LOG_TAG, "Button clicked !"); switch (button.getId()) { case R.id.insert_manual_smiley_button: insertSmileyOrTag(true, null); break; case R.id.insert_spoiler_button: insertSmileyOrTag(false, "spoiler"); break; case R.id.insert_image_button: insertSmileyOrTag(false, "img"); break; case R.id.insert_link_button: insertSmileyOrTag(false, "url"); break; case R.id.insert_quote_button: insertSmileyOrTag(false, "quote"); break; case R.id.make_text_bold_button: insertSmileyOrTag(false, "b"); break; case R.id.make_text_italic_button: insertSmileyOrTag(false, "i"); break; } } /** * Posts the reply on the server */ protected void postReply() { User activeUser = userManager.getActiveUser(); String message = replyEditText.getText().toString(); subscribe(replySubscriptionHandler.load(activeUser, mdService.replyToTopic(activeUser, currentTopic, message, true), new EndlessObserver<Response>() { @Override public void onNext(Response response) { if (response.isSuccessful()) { onReplySuccess(); } else { onReplyFailure(); } } @Override public void onError(Throwable throwable) { Log.e(LOG_TAG, "Unknown exception while replying", throwable); onReplyFailure(); } })); } protected void onReplySuccess() { clearResponseFromCache(userManager.getActiveUser()); // Flag that reply is successful to prevent it to be cached in the // response cache (onPause happens later in this activity lifecycle) setReplySuccessful(true); replyToActivity(RESULT_OK, false); } protected void onReplyFailure() { replyToActivity(UIConstants.REPLY_RESULT_KO, false); } protected void replyToActivity(int returnCode, boolean wasEdit) { Intent returnIntent = new Intent(); returnIntent.putExtra(UIConstants.ARG_REPLY_TOPIC, currentTopic); returnIntent.putExtra(UIConstants.ARG_REPLY_WAS_EDIT, wasEdit); setResult(returnCode, returnIntent); finish(); } /** * Animates the smiley selector back to its original position */ public void replaceSmileySelector() { smileysSelector.animate().translationYBy(smileySelectorTopOffset - smileysSelector.getY()).setDuration(150) .start(); } private void styleToolbarButtons(ViewGroup toolbar) { for (int i = 0; i < toolbar.getChildCount(); i++) { View childView = toolbar.getChildAt(i); if (childView instanceof ImageButton) { ImageButton imageButton = (ImageButton) childView; UiUtils.setDrawableColor(imageButton.getDrawable(), UiUtils.getReplyToolbarIconsColor(this)); } else if (childView instanceof HorizontalScrollView || childView instanceof LinearLayout) { styleToolbarButtons((ViewGroup) childView); } } } private void styleToolbarMenu(Toolbar toolbar) { for (int i = 0; i < toolbar.getMenu().size(); i++) { MenuItem menuItem = toolbar.getMenu().getItem(i); Drawable itemIcon = menuItem.getIcon(); if (itemIcon != null) { UiUtils.setDrawableColor(itemIcon, UiUtils.getReplyToolbarIconsColor(ReplyActivity.this)); } } } public Topic getCurrentTopic() { return currentTopic; } protected void clearResponseFromCache(User user) { responseStore.removeResponse(user, currentTopic); } public boolean isReplySuccessful() { return replyIsSuccessful; } public void setReplySuccessful(boolean replyIsSuccessful) { this.replyIsSuccessful = replyIsSuccessful; } private static class UserViewHolder { public TextView username; public ImageView avatar; } private class UserAdapter extends BindableAdapter<User> { private final List<User> users; private UserAdapter(Context context, List<User> users) { super(context); this.users = users; } @Override public View newView(LayoutInflater inflater, int position, ViewGroup container) { View convertView = getLayoutInflater().inflate(R.layout.dialog_reply_spinner_item, container, false); UserViewHolder viewHolder = new UserViewHolder(); viewHolder.username = (TextView) convertView.findViewById(R.id.user_username); viewHolder.avatar = (ImageView) convertView.findViewById(R.id.user_avatar); convertView.setTag(viewHolder); return convertView; } @Override public void bindView(User user, int position, View view) { UserViewHolder viewHolder = (UserViewHolder) view.getTag(); viewHolder.username.setText(user.getUsername()); viewHolder.avatar.setImageResource(R.drawable.profile_background_red); if (!user.isGuest()) { loadUserAvatarInto(user, viewHolder.avatar); } } @Override public int getCount() { return users.size(); } @Override public User getItem(int position) { return users.get(position); } @Override public long getItemId(int position) { return position; } } }