blob: 0137531e764617bc71a5a202a697971eac178c1a [file] [log] [blame]
package com.android.gallery3d.ui;
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import javax.microedition.khronos.opengles.GL11;
public class SlotView extends GLView {
private static final String TAG = "SlotView";
private static final int MAX_VELOCITY = 2500;
public static interface Model {
public int size();
public int getSlotHeight();
public int getSlotWidth();
public void putSlot(int index, int x, int y, DisplayItemPanel panel);
public void freeSlot(int index, DisplayItemPanel panel);
}
private Model mModel;
private final DisplayItemPanel mPanel;
private int mVerticalGap;
private int mHorizontalGap;
private int mSlotWidth;
private int mSlotHeight;
private int mRowCount;
private int mScrollLimit;
private int mVisibleStart = 0;
private int mVisibleEnd = 0;
private final GestureDetector mGestureDetector;
private final ScrollerHelper mScroller;
public SlotView(Context context) {
mPanel = new DisplayItemPanel();
super.addComponent(mPanel);
mGestureDetector =
new GestureDetector(context, new MyGestureListener());
mScroller = new ScrollerHelper(context, new DecelerateInterpolator(1));
}
@Override
public void addComponent(GLView view) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeComponent(GLView view) {
throw new UnsupportedOperationException();
}
public void setModel(Model model) {
if (model == mModel) return;
mModel = model;
if (model != null) initializeLayoutParams();
notifyDataChanged();
}
private void initializeLayoutParams() {
int size = mModel.size();
mSlotWidth = mModel.getSlotWidth();
mSlotHeight = mModel.getSlotHeight();
int totalHeight= getHeight();
int rowCount = totalHeight / mSlotHeight;
if (rowCount == 0) rowCount = 1;
mRowCount = rowCount;
int hGap = mHorizontalGap = 10;
int vGap = mVerticalGap = (
totalHeight - rowCount * mSlotHeight) / (rowCount + 1);
mScrollLimit = ((size + rowCount - 1) / rowCount) * (hGap + mSlotWidth)
+ hGap - getWidth();
if (mScrollLimit < 0) mScrollLimit = 0;
}
@Override
protected void onLayout(boolean changeSize, int l, int t, int r, int b) {
mPanel.layout(0, 0, r - l, b - t);
mVisibleStart = 0;
mVisibleEnd = 0;
mPanel.prepareTransition();
initializeLayoutParams();
// The scroll limit could be changed
setScrollPosition(mPanel.mScrollX, true);
mPanel.startTransition();
}
private void setScrollPosition(int position, boolean force) {
position = Util.clamp(position, 0, mScrollLimit);
if (!force && position == mPanel.mScrollX) return;
mPanel.mScrollX = position;
int colWidth = mHorizontalGap + mSlotWidth;
int rowHeight = mVerticalGap + mSlotHeight;
int startColumn = position / colWidth;
int endColumn = (position + getWidth() + mSlotWidth - 1) / colWidth;
setVisibleRange(startColumn, endColumn);
invalidate();
}
public void notifyDataChanged() {
setScrollPosition(0, false);
notifyDataInvalidate();
}
public void notifyDataInvalidate() {
invalidate();
}
@Override
protected boolean dispatchTouchEvent(MotionEvent event) {
// Don't pass the event to the child (MediaDisplayPanel).
// Handle it here
return onTouch(event);
}
@Override
protected boolean onTouch(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mScroller.forceFinished();
break;
}
return true;
}
@Override
protected void render(GLRootView root, GL11 gl) {
super.render(root, gl);
if (mScroller.computeScrollOffset(root.currentAnimationTimeMillis())) {
setScrollPosition(mScroller.getCurrentPosition(), false);
invalidate();
}
}
private void freeSlotsInColumn(int columnIndex) {
int rowCount = mRowCount;
DisplayItemPanel panel = mPanel;
for (int i = columnIndex * rowCount,
n = Math.min(mModel.size(), i + rowCount); i < n; ++i) {
mModel.freeSlot(i, panel);
}
}
private void putSlotsInColumn(int columnIndex) {
int x = columnIndex * (mHorizontalGap + mSlotWidth) + mHorizontalGap;
int y = mVerticalGap;
int rowHeight = mVerticalGap + mSlotHeight;
int rowCount = mRowCount;
DisplayItemPanel panel = mPanel;
for (int i = columnIndex * rowCount,
n = Math.min(mModel.size(), i + rowCount); i < n; ++i) {
mModel.putSlot(i, x, y, panel);
y += rowHeight;
}
}
// start: inclusive, end: exclusive
private void setVisibleRange(int start, int end) {
if (start == mVisibleStart && end == mVisibleEnd) return;
int rowCount = mRowCount;
if (start >= mVisibleEnd || end < mVisibleStart) {
for (int i = mVisibleStart, n = mVisibleEnd; i < n; ++i) {
freeSlotsInColumn(i);
}
for (int i = start; i < end; ++i) {
putSlotsInColumn(i);
}
} else {
for (int i = mVisibleStart; i < start; ++i) {
freeSlotsInColumn(i);
}
for (int i = end, n = mVisibleEnd; i < n; ++i) {
freeSlotsInColumn(i);
}
for (int i = start, n = mVisibleStart; i < n; ++i) {
putSlotsInColumn(i);
}
for (int i = mVisibleEnd; i < end; ++i) {
putSlotsInColumn(i);
}
}
mVisibleStart = start;
mVisibleEnd = end;
}
private class MyGestureListener
extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1,
MotionEvent e2, float velocityX, float velocityY) {
if (mScrollLimit == 0) return false;
velocityX = Util.clamp(velocityX, -MAX_VELOCITY, MAX_VELOCITY);
mScroller.fling(mPanel.mScrollX, -(int) velocityX, 0, mScrollLimit);
invalidate();
return true;
}
@Override
public boolean onScroll(MotionEvent e1,
MotionEvent e2, float distanceX, float distanceY) {
if (mScrollLimit == 0) return false;
setScrollPosition(mPanel.mScrollX + (int) distanceX, false);
invalidate();
return true;
}
}
}