Add board navigation to toolbar.

tempwork
Floens 11 years ago
parent d7ffe9e8e8
commit b8aa6dc3aa
  1. 13
      Clover/Clover.iml
  2. 74
      Clover/app/app.iml
  3. 2
      Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java
  4. 22
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  5. 135
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  6. 84
      Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java
  7. 1
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java
  8. 74
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java
  9. 2
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java
  10. 84
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuSubMenu.java
  11. 33
      Clover/app/src/main/res/layout/toolbar_menu.xml
  12. 5
      Clover/app/src/main/res/layout/toolbar_menu_item.xml

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -1,74 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="Clover" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="beta" />
<option name="ASSEMBLE_TASK_NAME" value="assembleBeta" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileBetaJava" />
<option name="SOURCE_GEN_TASK_NAME" value="generateBetaSources" />
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/classes/beta" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/source/r/beta" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/aidl/beta" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/beta" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/rs/beta" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/res/rs/beta" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/beta/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/beta/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/beta/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/beta/assets" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/release/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/beta/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/beta/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/apk" />
<excludeFolder url="file://$MODULE_DIR$/build/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/res" />
<excludeFolder url="file://$MODULE_DIR$/build/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="ormlite-android-4.48" level="project" />
<orderEntry type="library" exported="" name="jsoup-1.7.3" level="project" />
<orderEntry type="library" exported="" name="ormlite-core-4.48" level="project" />
<orderEntry type="library" exported="" name="httpclientandroidlib-1.1.2" level="project" />
<orderEntry type="library" exported="" name="android-support-v13" level="project" />
</component>
</module>

@ -145,6 +145,8 @@ public abstract class NavigationController extends Controller implements Control
for (Controller controller : controllerList) { for (Controller controller : controllerList) {
controller.onConfigurationChanged(newConfig); controller.onConfigurationChanged(newConfig);
} }
toolbar.onConfigurationChanged(newConfig);
} }
public void initWithController(final Controller controller) { public void initWithController(final Controller controller) {

@ -51,19 +51,27 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
} }
public void bindLoadable(Loadable loadable) { public void bindLoadable(Loadable loadable) {
if (!loadable.equals(this.loadable)) { if (chanLoader == null) {
if (this.loadable != null) { if (!loadable.equals(this.loadable)) {
unbindLoadable(); if (this.loadable != null) {
} unbindLoadable();
}
this.loadable = loadable; this.loadable = loadable;
chanLoader = LoaderPool.getInstance().obtain(loadable, this); chanLoader = LoaderPool.getInstance().obtain(loadable, this);
}
} }
} }
public void unbindLoadable() { public void unbindLoadable() {
threadPresenterCallback.showLoading(); if (chanLoader != null) {
LoaderPool.getInstance().release(chanLoader, this);
chanLoader = null;
loadable = null;
threadPresenterCallback.showLoading();
}
} }
public void requestData() { public void requestData() {

@ -18,10 +18,19 @@
package org.floens.chan.ui.controller; package org.floens.chan.ui.controller;
import android.content.Context; import android.content.Context;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import org.floens.chan.ChanApplication;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.chan.ChanUrls; import org.floens.chan.chan.ChanUrls;
import org.floens.chan.controller.Controller; import org.floens.chan.controller.Controller;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Loadable;
import org.floens.chan.ui.layout.ThreadLayout; import org.floens.chan.ui.layout.ThreadLayout;
import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenu;
@ -33,7 +42,7 @@ import org.floens.chan.utils.AndroidUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class BrowseController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback { public class BrowseController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, ToolbarMenuSubMenu.ToolbarMenuItemSubMenuCallback, BoardManager.BoardChangeListener {
private static final int REFRESH_ID = 1; private static final int REFRESH_ID = 1;
private static final int POST_ID = 2; private static final int POST_ID = 2;
private static final int SEARCH_ID = 101; private static final int SEARCH_ID = 101;
@ -41,6 +50,7 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool
private static final int SETTINGS_ID = 103; private static final int SETTINGS_ID = 103;
private ThreadLayout threadLayout; private ThreadLayout threadLayout;
private List<ToolbarMenuSubItem> boardItems;
public BrowseController(Context context) { public BrowseController(Context context) {
super(context); super(context);
@ -50,6 +60,12 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
ChanApplication.getBoardManager().addListener(this);
navigationItem.middleMenu = new ToolbarMenuSubMenu(context);
navigationItem.middleMenu.setCallback(this);
loadBoards();
navigationItem.title = "Hello world"; navigationItem.title = "Hello world";
ToolbarMenu menu = new ToolbarMenu(context); ToolbarMenu menu = new ToolbarMenu(context);
navigationItem.menu = menu; navigationItem.menu = menu;
@ -72,13 +88,14 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool
view = threadLayout; view = threadLayout;
Loadable loadable = new Loadable("g"); loadBoard(ChanApplication.getBoardManager().getSavedBoards().get(0));
loadable.mode = Loadable.Mode.CATALOG; }
loadable.generateTitle();
navigationItem.title = loadable.title;
threadLayout.getPresenter().bindLoadable(loadable); @Override
threadLayout.getPresenter().requestData(); public void onDestroy() {
super.onDestroy();
ChanApplication.getBoardManager().removeListener(this);
} }
@Override @Override
@ -110,10 +127,112 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool
} }
} }
@Override
public void onSubMenuItemClicked(ToolbarMenuSubMenu menu, ToolbarMenuSubItem item) {
if (menu == navigationItem.middleMenu) {
if (item instanceof ToolbarMenuSubItemBoard) {
loadBoard(((ToolbarMenuSubItemBoard) item).board);
navigationController.toolbar.updateNavigation();
} else {
// TODO start board editor
}
}
}
@Override @Override
public void openThread(Loadable threadLoadable) { public void openThread(Loadable threadLoadable) {
ViewThreadController viewThreadController = new ViewThreadController(context); ViewThreadController viewThreadController = new ViewThreadController(context);
viewThreadController.setLoadable(threadLoadable); viewThreadController.setLoadable(threadLoadable);
navigationController.pushController(viewThreadController); navigationController.pushController(viewThreadController);
} }
@Override
public void onBoardsChanged() {
loadBoards();
}
private void loadBoard(Board board) {
Loadable loadable = new Loadable(board.value);
loadable.mode = Loadable.Mode.CATALOG;
loadable.generateTitle();
navigationItem.title = board.key;
threadLayout.getPresenter().unbindLoadable();
threadLayout.getPresenter().bindLoadable(loadable);
threadLayout.getPresenter().requestData();
for (ToolbarMenuSubItem item : boardItems) {
if (((ToolbarMenuSubItemBoard) item).board == board) {
navigationItem.middleMenu.setSelectedItem(item);
}
}
}
private void loadBoards() {
List<Board> boards = ChanApplication.getBoardManager().getSavedBoards();
boardItems = new ArrayList<>();
for (Board board : boards) {
ToolbarMenuSubItem item = new ToolbarMenuSubItemBoard(board);
boardItems.add(item);
}
navigationItem.middleMenu.setItems(boardItems);
navigationItem.middleMenu.setAdapter(new BoardsAdapter(context, boardItems));
}
private static class ToolbarMenuSubItemBoard extends ToolbarMenuSubItem {
public Board board;
public ToolbarMenuSubItemBoard(Board board) {
super(board.id, board.key);
this.board = board;
}
}
private static class BoardsAdapter extends BaseAdapter {
private final Context context;
private List<ToolbarMenuSubItem> items;
public BoardsAdapter(Context context, List<ToolbarMenuSubItem> items) {
this.context = context;
this.items = items;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) LayoutInflater.from(context).inflate(R.layout.toolbar_menu_item, parent, false);
textView.setText(getItem(position));
if (position < items.size()) {
textView.setTypeface(AndroidUtils.ROBOTO_MEDIUM);
} else {
textView.setTypeface(AndroidUtils.ROBOTO_MEDIUM, Typeface.ITALIC);
}
return textView;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getDropDownView(position, convertView, parent);
}
@Override
public int getCount() {
return items.size() + 1;
}
@Override
public String getItem(int position) {
if (position >= 0 && position < items.size()) {
return items.get(position).getText();
} else {
return context.getString(R.string.board_select_add);
}
}
@Override
public long getItemId(int position) {
return position;
}
}
} }

@ -0,0 +1,84 @@
package org.floens.chan.ui.drawable;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import static org.floens.chan.utils.AndroidUtils.dp;
public class DropdownArrowDrawable extends Drawable {
private Paint paint = new Paint();
private Path path = new Path();
private int width;
private int height;
public DropdownArrowDrawable() {
width = dp(12);
height = dp(6);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0xffffffff);
path.moveTo(0, 0);
path.lineTo(width, 0);
path.lineTo(width / 2, height);
path.lineTo(0, 0);
path.close();
}
@Override
public void draw(Canvas canvas) {
canvas.drawPath(path, paint);
}
@Override
public int getIntrinsicWidth() {
return width;
}
@Override
public int getIntrinsicHeight() {
return height;
}
@Override
protected boolean onStateChange(int[] states) {
boolean pressed = false;
for (int state : states) {
if (state == android.R.attr.state_pressed) {
pressed = true;
}
}
int color = pressed ? 0x88ffffff : 0xffffffff;
if (color != paint.getColor()) {
paint.setColor(color);
invalidateSelf();
return true;
} else {
return false;
}
}
@Override
public boolean isStateful() {
return true;
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}

@ -24,4 +24,5 @@ public class NavigationItem {
public ToolbarMenu menu; public ToolbarMenu menu;
public boolean hasBack = true; public boolean hasBack = true;
public LinearLayout view; public LinearLayout view;
public ToolbarMenuSubMenu middleMenu;
} }

@ -23,12 +23,13 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.text.TextUtils; import android.support.v4.view.GravityCompat;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.animation.DecelerateInterpolator; import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@ -38,6 +39,7 @@ import android.widget.TextView;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.ui.drawable.ArrowMenuDrawable; import org.floens.chan.ui.drawable.ArrowMenuDrawable;
import org.floens.chan.ui.drawable.DropdownArrowDrawable;
import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.AndroidUtils;
import java.util.ArrayList; import java.util.ArrayList;
@ -70,6 +72,10 @@ public class Toolbar extends LinearLayout implements View.OnClickListener {
init(); init();
} }
public void updateNavigation() {
setNavigationItem(false, false, navigationItem);
}
public void setNavigationItem(final boolean animate, final boolean pushing, final NavigationItem item) { public void setNavigationItem(final boolean animate, final boolean pushing, final NavigationItem item) {
if (item.menu != null) { if (item.menu != null) {
AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() { AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() {
@ -100,6 +106,9 @@ public class Toolbar extends LinearLayout implements View.OnClickListener {
arrowMenuDrawable.setProgress(progress); arrowMenuDrawable.setProgress(progress);
} }
public void onConfigurationChanged(Configuration newConfig) {
}
private void init() { private void init() {
setOrientation(HORIZONTAL); setOrientation(HORIZONTAL);
@ -128,12 +137,19 @@ public class Toolbar extends LinearLayout implements View.OnClickListener {
} }
private void setNavigationItemView(boolean animate, boolean pushing, NavigationItem toItem) { private void setNavigationItemView(boolean animate, boolean pushing, NavigationItem toItem) {
final NavigationItem fromItem = navigationItem;
if (!animate) {
if (fromItem != null) {
removeNavigationItem(fromItem);
}
setArrowMenuProgress(toItem.hasBack ? 1f : 0f);
}
toItem.view = createNavigationItemView(toItem); toItem.view = createNavigationItemView(toItem);
navigationItemContainer.addView(toItem.view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); navigationItemContainer.addView(toItem.view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
final NavigationItem fromItem = navigationItem;
final int duration = 300; final int duration = 300;
final int offset = dp(16); final int offset = dp(16);
@ -191,12 +207,6 @@ public class Toolbar extends LinearLayout implements View.OnClickListener {
set.setStartDelay(pushing ? 0 : 100); set.setStartDelay(pushing ? 0 : 100);
set.playTogether(animations); set.playTogether(animations);
set.start(); set.start();
} else {
// No animation
if (fromItem != null) {
removeNavigationItem(fromItem);
}
setArrowMenuProgress(toItem.hasBack ? 1f : 0f);
} }
navigationItem = toItem; navigationItem = toItem;
@ -208,26 +218,42 @@ public class Toolbar extends LinearLayout implements View.OnClickListener {
item.view = null; item.view = null;
} }
private LinearLayout createNavigationItemView(NavigationItem item) { private LinearLayout createNavigationItemView(final NavigationItem item) {
LinearLayout wrapper = new LinearLayout(getContext()); LinearLayout wrapper = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.toolbar_menu, null);
wrapper.setGravity(Gravity.CENTER_VERTICAL);
TextView titleView = new TextView(getContext());
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); final TextView titleView = (TextView) wrapper.findViewById(R.id.title);
// titleView.setTextColor(Color.argb((int)(0.87 * 255.0), 0, 0, 0));
titleView.setTextColor(Color.argb(255, 255, 255, 255));
titleView.setGravity(Gravity.CENTER_VERTICAL);
titleView.setSingleLine(true);
titleView.setLines(1);
titleView.setEllipsize(TextUtils.TruncateAt.END);
titleView.setPadding(dp(16), 0, 0, 0);
titleView.setTypeface(AndroidUtils.ROBOTO_MEDIUM); titleView.setTypeface(AndroidUtils.ROBOTO_MEDIUM);
titleView.setText(item.title); titleView.setText(item.title);
wrapper.addView(titleView, new LayoutParams(0, LayoutParams.MATCH_PARENT, 1f)); // black: titleView.setTextColor(Color.argb((int)(0.87 * 255.0), 0, 0, 0));
if (item.middleMenu != null) {
item.middleMenu.setAnchor(titleView, GravityCompat.END | Gravity.TOP, dp(5), dp(5)); // TODO gravity left doesn't work
Drawable drawable = new DropdownArrowDrawable();
titleView.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null);
titleView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
item.middleMenu.show();
}
});
}
if (item.menu != null) { if (item.menu != null) {
wrapper.addView(item.menu, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); wrapper.addView(item.menu, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
} }
AndroidUtils.waitForMeasure(titleView, new AndroidUtils.OnMeasuredCallback() {
@Override
public void onMeasured(View view, int width, int height) {
if (item.middleMenu != null) {
item.middleMenu.setPopupWidth(Math.max(dp(150), titleView.getWidth()));
}
}
});
return wrapper; return wrapper;
} }

@ -91,7 +91,7 @@ public class ToolbarMenuItem implements View.OnClickListener, ToolbarMenuSubMenu
} }
@Override @Override
public void onSubMenuItemClicked(ToolbarMenuSubItem item) { public void onSubMenuItemClicked(ToolbarMenuSubMenu menu, ToolbarMenuSubItem item) {
callback.onSubMenuItemClicked(this, item); callback.onSubMenuItemClicked(this, item);
} }

@ -18,6 +18,7 @@
package org.floens.chan.ui.toolbar; package org.floens.chan.ui.toolbar;
import android.content.Context; import android.content.Context;
import android.support.v4.view.GravityCompat;
import android.support.v7.widget.ListPopupWindow; import android.support.v7.widget.ListPopupWindow;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -26,6 +27,7 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.PopupWindow; import android.widget.PopupWindow;
import android.widget.TextView; import android.widget.TextView;
@ -39,43 +41,101 @@ import static org.floens.chan.utils.AndroidUtils.dp;
public class ToolbarMenuSubMenu { public class ToolbarMenuSubMenu {
private final Context context; private final Context context;
private final View anchor; private View anchor;
private int anchorGravity;
private int anchorOffsetX;
private int anchorOffsetY;
private int popupWidth = -1;
private List<ToolbarMenuSubItem> items; private List<ToolbarMenuSubItem> items;
private ToolbarMenuSubItem selectedItem;
private ListAdapter adapter;
private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener; private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener;
private ListPopupWindow popupWindow;
private ToolbarMenuItemSubMenuCallback callback; private ToolbarMenuItemSubMenuCallback callback;
public ToolbarMenuSubMenu(Context context, View anchor, List<ToolbarMenuSubItem> items) { public ToolbarMenuSubMenu(Context context, View anchor, List<ToolbarMenuSubItem> items) {
this.context = context; this.context = context;
this.anchor = anchor; this.anchor = anchor;
anchorGravity = Gravity.RIGHT | Gravity.TOP;
anchorOffsetX = -dp(5);
anchorOffsetY = dp(5);
this.items = items; this.items = items;
} }
public ToolbarMenuSubMenu(Context context) {
this.context = context;
}
public void setAnchor(View anchor, int anchorGravity, int anchorOffsetX, int anchorOffsetY) {
this.anchor = anchor;
this.anchorGravity = anchorGravity;
this.anchorOffsetX = anchorOffsetX;
this.anchorOffsetY = anchorOffsetY;
}
public void setPopupWidth(int width) {
this.popupWidth = width;
if (popupWindow != null) {
popupWindow.setContentWidth(popupWidth);
}
}
public void setItems(List<ToolbarMenuSubItem> items) {
this.items = items;
if (popupWindow != null) {
popupWindow.dismiss();
}
}
public void setSelectedItem(ToolbarMenuSubItem item) {
this.selectedItem = item;
}
public void setAdapter(ListAdapter adapter) {
this.adapter = adapter;
}
public void setCallback(ToolbarMenuItemSubMenuCallback callback) { public void setCallback(ToolbarMenuItemSubMenuCallback callback) {
this.callback = callback; this.callback = callback;
} }
public void show() { public void show() {
final ListPopupWindow popupWindow = new ListPopupWindow(context); popupWindow = new ListPopupWindow(context);
popupWindow.setAnchorView(anchor); popupWindow.setAnchorView(anchor);
popupWindow.setModal(true); popupWindow.setModal(true);
popupWindow.setDropDownGravity(Gravity.RIGHT | Gravity.TOP); popupWindow.setDropDownGravity(GravityCompat.END | Gravity.TOP);
popupWindow.setVerticalOffset(-anchor.getHeight() + dp(5)); popupWindow.setVerticalOffset(-anchor.getHeight() + anchorOffsetY);
popupWindow.setHorizontalOffset(-dp(5)); popupWindow.setHorizontalOffset(anchorOffsetX);
popupWindow.setContentWidth(dp(3 * 56)); if (popupWidth > 0) {
popupWindow.setContentWidth(popupWidth);
} else {
popupWindow.setContentWidth(dp(3 * 56));
}
List<String> stringItems = new ArrayList<>(items.size()); List<String> stringItems = new ArrayList<>(items.size());
for (ToolbarMenuSubItem item : items) { int selectedPosition = 0;
stringItems.add(item.getText()); for (int i = 0; i < items.size(); i++) {
stringItems.add(items.get(i).getText());
if (items.get(i) == selectedItem) {
selectedPosition = i;
}
}
if (adapter != null) {
popupWindow.setAdapter(adapter);
} else {
popupWindow.setAdapter(new SubMenuArrayAdapter(context, R.layout.toolbar_menu_item, stringItems));
} }
popupWindow.setAdapter(new SubMenuArrayAdapter(context, R.layout.toolbar_menu_item, stringItems));
popupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { popupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position >= 0 && position < items.size()) { if (position >= 0 && position < items.size()) {
callback.onSubMenuItemClicked(items.get(position)); callback.onSubMenuItemClicked(ToolbarMenuSubMenu.this, items.get(position));
popupWindow.dismiss(); popupWindow.dismiss();
} else {
callback.onSubMenuItemClicked(ToolbarMenuSubMenu.this, null);
} }
} }
}); });
@ -98,14 +158,16 @@ public class ToolbarMenuSubMenu {
anchor.getViewTreeObserver().removeGlobalOnLayoutListener(globalLayoutListener); anchor.getViewTreeObserver().removeGlobalOnLayoutListener(globalLayoutListener);
} }
globalLayoutListener = null; globalLayoutListener = null;
popupWindow = null;
} }
}); });
popupWindow.show(); popupWindow.show();
popupWindow.setSelection(selectedPosition);
} }
public interface ToolbarMenuItemSubMenuCallback { public interface ToolbarMenuItemSubMenuCallback {
public void onSubMenuItemClicked(ToolbarMenuSubItem item); public void onSubMenuItemClicked(ToolbarMenuSubMenu menu, ToolbarMenuSubItem item);
} }
private static class SubMenuArrayAdapter extends ArrayAdapter<String> { private static class SubMenuArrayAdapter extends ArrayAdapter<String> {

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:baselineAligned="false">
<FrameLayout
android:id="@+id/title_container"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="20dp"
android:textColor="#ffffffff"
android:gravity="center_vertical"
android:singleLine="true"
android:lines="1"
android:ellipsize="end"
android:minWidth="150dp"
android:paddingLeft="16dp"
android:drawablePadding="16dp" />
</FrameLayout>
<!-- menu inserted here
TODO: make this layout relative and make a ViewStub but with programatically added views -->
</LinearLayout>

@ -6,4 +6,7 @@
android:paddingLeft="16dp" android:paddingLeft="16dp"
android:paddingRight="16dp" android:paddingRight="16dp"
android:textColor="#ff000000" android:textColor="#ff000000"
android:textSize="16sp" /> android:textSize="16sp"
android:singleLine="true"
android:lines="1"
android:ellipsize="end" />

Loading…
Cancel
Save