Страница 1 из 1
Зум
Добавлено: 24 июн 2013, 17:31
Banhammer
как реализовать зум ImageView через нажатие?
я может идиот, но ей богу нигде не вижу ничего на эту тему)))
тоесть есть ImageView, в нём есть картинка из ресурсов, вешаем на компоненту обработчик нажатия, а что дальше?)
как я думаю, нужно баловаться с xml параметрами, но чтогдекогда, какой это даст результат, как себя это будет вести, нужно ли ложить ImageView в Scroll, или нет в этом необходимости... много вопросов, очень много.
Расскажи кто-нить опытный пожалуйста, дико интересно.
Или быть может есть какой-то стандартный класс для таких задач?
Какой способ лучше, с какой версии что будет работать и тд..
Re: Зум
Добавлено: 24 июн 2013, 17:47
rezak90
Вам какой зум нужен? если мультитач (ну когда пару пальцев и приближаем/увеличиваем) то это не проблема, то дам готовый класс.
Re: Зум
Добавлено: 24 июн 2013, 17:53
Banhammer
ну насколько я знаю, мультитач работает с определённой версии андроида(например на моём телефоне он есть, а на телефоне моей мадамы нету). как технология, мне это тоже интересно, но в данный момент не хочется лезть вперёд, поэтому интересуют больше махинации типа нажал->на 50% увеличилось изображение, нажал-> уменьшилось. пока что этого мне хватит)))
а вообще нужны любые зумы, статьи, книжки, топики, я бы всё почитал с удовольствием, но только по порядку: от элементарного к тяжёлому)
Re: Зум
Добавлено: 24 июн 2013, 18:04
rezak90
мультитач от версии не зависит, это может смартфон не поддерживает мультитач, но я таких не видел, если остались такие то это раритер)))
вся магия заключается в ScaleView:
Код: Выделить всё
public class ScaleView extends View {
private static final int INVALID_POINTER_ID = -1;
private Drawable mIcon;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
private BitmapFactoryHack bfh = new BitmapFactoryHack(true);
private static final String TAG = "ScaleView";
private Context context;
public ScaleView(Context context, String _path) {
this(context, null, 0, _path);
this.context = context;
}
public void clear() {
bfh.freeAll();
}
public ScaleView(Context context, AttributeSet attrs, String _path) {
this(context, attrs, 0, _path);
this.context = context;
}
@SuppressWarnings("deprecation")
public ScaleView(Context context, AttributeSet attrs, int defStyle,
String path) {
super(context, attrs, defStyle);
this.context = context;
try {
mIcon = new BitmapDrawable(bfh.alloc(FileManager.decrypt(
FileManager.getFullPathForFile(path, context), context),
path));
} catch (IOException e) {
Log.d(TAG, "IOException");
} catch(NullPointerException ex) {
}
if (mIcon == null) {
Log.d(TAG, "mIcon is null");
Toast.makeText(context, R.string.osi_outofmemoryerror,
Toast.LENGTH_SHORT);
return;
}
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(),
mIcon.getIntrinsicHeight());
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
try {
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
} catch (NullPointerException e) {
Log.d(TAG, "mIcon is null");
Toast.makeText(context, R.string.osi_outofmemoryerror,
Toast.LENGTH_SHORT);
return true;
}
return true;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
try {
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
mIcon.draw(canvas);
canvas.restore();
} catch (NullPointerException e) {
Log.d(TAG, "mIcon is null");
Toast.makeText(context, R.string.osi_outofmemoryerror,
Toast.LENGTH_SHORT);
}
}
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
далее активити:
Код: Выделить всё
public class OSIActivity extends BaseActivity {
public static final String F_PATH = "path";
private ScaleView view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onResume() {
String path = getIntent().getExtras().getString(F_PATH);
view = new ScaleView(this, path);
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(view);
super.onResume();
}
}
Класс BitmapFactoryHack:
Код: Выделить всё
public class BitmapFactoryHack
{
private final VMRuntimeHack runtime = new VMRuntimeHack();
private final boolean useHack;
private Set<Bitmap> allocatedBitmaps = new HashSet<Bitmap>();
private HashMap<String, SoftReference<Bitmap>> hackedBitmaps = new HashMap<String, SoftReference<Bitmap>>();
public BitmapFactoryHack(boolean useHack)
{
this.useHack = useHack;
}
public Bitmap alloc(Bitmap bitmap, String key)
{
Bitmap bmp = bitmap;
if (useHack)
{
if (!hackedBitmaps.containsKey(key))
{
runtime.trackFree(bmp.getRowBytes() * bmp.getHeight());
hackedBitmaps.put(key, new SoftReference<Bitmap>(bmp));
}
}
allocatedBitmaps.add(bmp);
return bmp;
}
public int getCount()
{
return hackedBitmaps.size();
}
public void free(Bitmap bmp)
{
bmp.recycle();
System.gc();
System.gc();
// if (hackedBitmaps.containsValue(bmp)) {
runtime.trackAlloc(bmp.getRowBytes() * bmp.getHeight());
hackedBitmaps.remove(bmp);
// }
}
public void freeAll()
{
hackedBitmaps.clear();
for (Bitmap bmp : new LinkedList<Bitmap>(allocatedBitmaps))
free(bmp);
}
public Bitmap getBitmap(String key)
{
if (hackedBitmaps.containsKey(key))
{
return hackedBitmaps.get(key).get();
}
return null;
}
class VMRuntimeHack
{
private Object runtime = null;
private Method trackAllocation = null;
private Method trackFree = null;
private static final String TAG = "VMRuntimeHack";
public boolean trackAlloc(long size)
{
if (runtime == null)
return false;
try
{
Object res = trackAllocation.invoke(runtime, Long.valueOf(size));
return (res instanceof Boolean) ? (Boolean) res : true;
}
catch (IllegalArgumentException e)
{
return false;
}
catch (IllegalAccessException e)
{
return false;
}
catch (InvocationTargetException e)
{
return false;
}
}
public boolean trackFree(long size)
{
if (runtime == null)
return false;
try
{
Object res = trackFree.invoke(runtime, Long.valueOf(size));
return (res instanceof Boolean) ? (Boolean) res : true;
}
catch (IllegalArgumentException e)
{
return false;
}
catch (IllegalAccessException e)
{
return false;
}
catch (InvocationTargetException e)
{
return false;
}
}
public VMRuntimeHack()
{
boolean success = false;
try
{
Class cl = Class.forName("dalvik.system.VMRuntime");
Method getRt = cl.getMethod("getRuntime", new Class[0]);
runtime = getRt.invoke(null, new Object[0]);
trackAllocation = cl.getMethod("trackExternalAllocation", new Class[] { long.class });
trackFree = cl.getMethod("trackExternalFree", new Class[] { long.class });
success = true;
}
catch (ClassNotFoundException e)
{
}
catch (SecurityException e)
{
}
catch (NoSuchMethodException e)
{
}
catch (IllegalArgumentException e)
{
}
catch (IllegalAccessException e)
{
}
catch (InvocationTargetException e)
{
}
if (!success)
{
Log.e(TAG, "VMRuntime hack does not work!");
runtime = null;
trackAllocation = null;
trackFree = null;
}
}
}
}
Re: Зум
Добавлено: 24 июн 2013, 18:26
Banhammer
омг))
BitmapFactoryHack, не существует класс оО, он библиотечный?
Re: Зум
Добавлено: 24 июн 2013, 18:27
KamiSempai
Для отлова момента зумирования можно использовать ScaleGestureDetector
Re: Зум
Добавлено: 24 июн 2013, 18:28
rezak90
KamiSempai писал(а):Для отлова момента зумирования можно использовать ScaleGestureDetector
сейчас да, но этот класс ScaleView я ещё год назад нашёл на гите))
Re: Зум
Добавлено: 24 июн 2013, 18:30
rezak90
Banhammer писал(а):омг))
BitmapFactoryHack, не существует класс оО, он библиотечный?
обновил
Re: Зум
Добавлено: 24 июн 2013, 19:32
Banhammer
так, немножко разобрался, пересмотрел кучу реализаций и тд, что сразу бросается в глаза, так это вот эта строчка
setContentView(view);
тоесть нет возможности просто взять ImageView и извращать его? нужно обязательно создавать свой View?
Re: Зум
Добавлено: 24 июн 2013, 19:36
Banhammer
rezak90 писал(а):Banhammer писал(а):омг))
BitmapFactoryHack, не существует класс оО, он библиотечный?
обновил
и FileManager тоже не определяется, он тоже не библиотечный?
нехилая такая реализация мультитач зума, аж 3 класса левых =(
Re: Зум
Добавлено: 25 июн 2013, 16:44
Banhammer
да ладно ребят, неужели никто не работал с зумом? или эта инфа засекречена в нашей стране?)
Re: Зум
Добавлено: 25 июн 2013, 17:35
rezak90
Banhammer писал(а):rezak90 писал(а):Banhammer писал(а):омг))
BitmapFactoryHack, не существует класс оО, он библиотечный?
обновил
и FileManager тоже не определяется, он тоже не библиотечный?
нехилая такая реализация мультитач зума, аж 3 класса левых =(
можно ж было догадаться что метод getFullPathForFile возвращает абсолютный путь к файлу и написать его самому:
Код: Выделить всё
public static String getFullPathForFile(String fileName, Context context)
{
if (context.getExternalFilesDir(null) != null)
{
if (fileName == null)
{
return getAppFolderSD(context).toString();
}
return String.format("%s/%s", getAppFolderSD(context).toString(), fileName);
}
else
{
if (fileName == null)
{
return context.getExternalFilesDir(null).toString();
}
return new File(context.getExternalFilesDir(null), fileName).getAbsolutePath();
}
}
private static File getAppFolderSD(Context context)
{
File file = context.getExternalFilesDir(null);
if (file == null)
{
file = context.getFilesDir();
}
if (!file.exists())
{
file.mkdir();
}
return file;
}
он Вам в принципи и не нужен.
Re: Зум
Добавлено: 25 июн 2013, 19:10
Banhammer
вот нашёл интересную реализацию, как раз подходящую под абсолютно все мои нужды
Код: Выделить всё
imageDetail.setOnTouchListener(new View.OnTouchListener() {
private String TAG="lal";
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
System.out.println("matrix=" + savedMatrix.toString());
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
startPoint.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.w(TAG, "CASE: ACTION POINTER DAWN");
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(midPoint, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.w(TAG, "CASE: ACTION POINTER UP");
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - startPoint.x,
event.getY() - startPoint.y);
} else if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, midPoint.x, midPoint.y);
}
}
break;
}
view.setImageMatrix(matrix);
return true;
}
@SuppressLint("FloatMath")
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
});
но тут есть минус, изображение может вылазить за экран, тоесть его можно *выкинуть* из поля действия пользователя
есть возможность это решить в данной реализации?
Re: Зум
Добавлено: 25 июн 2013, 19:52
rezak90
я нормальный пример привёл, чем не нравится?
Re: Зум
Добавлено: 25 июн 2013, 20:17
Banhammer
меня расстраивает реализация через новый view)))) для меня в данный момент: момент, когда уже написано приложение, очень подходит именно переопределение OnTouchListener.
вставил кусок кода, ура, кидай друзьям все пользуются счастливые)
Re: Зум
Добавлено: 25 июн 2013, 22:51
rezak90
Banhammer писал(а):меня расстраивает реализация через новый view)))) для меня в данный момент: момент, когда уже написано приложение, очень подходит именно переопределение OnTouchListener.
вставил кусок кода, ура, кидай друзьям все пользуются счастливые)
я вижу на сколько такой код удобен))) попробуйте в ддмс посмотреть объёмы памяти при зуме.
Re: Зум
Добавлено: 25 июн 2013, 23:01
Banhammer
ну если ты про то, что зум ничерта не контролируемый и займёт просто уйму памяти, то да, тут есть грех)))
по интеграции в код, это удобно, всунул и збс, а в твоём варианте ещё пути какие-то, всё сложно и опасно))) я остерегаюсь таких моментов в начале своего пути))
Re: Зум
Добавлено: 25 июн 2013, 23:40
rezak90
Banhammer писал(а):ну если ты про то, что зум ничерта не контролируемый и займёт просто уйму памяти, то да, тут есть грех)))
по интеграции в код, это удобно, всунул и збс, а в твоём варианте ещё пути какие-то, всё сложно и опасно))) я остерегаюсь таких моментов в начале своего пути))
те пути что у меня, то я просто со своего приложения копировал, там так дела обстоят... а на счёт памяти советую протестировать что бы не вылезло боком потом
Re: Зум
Добавлено: 25 июн 2013, 23:53
Banhammer
rezak90 писал(а):Banhammer писал(а):ну если ты про то, что зум ничерта не контролируемый и займёт просто уйму памяти, то да, тут есть грех)))
по интеграции в код, это удобно, всунул и збс, а в твоём варианте ещё пути какие-то, всё сложно и опасно))) я остерегаюсь таких моментов в начале своего пути))
те пути что у меня, то я просто со своего приложения копировал, там так дела обстоят... а на счёт памяти советую протестировать что бы не вылезло боком потом
ну я понимаю все замуты с памятью и слёзками, связанными с ними, но что поделать?) тут покрайней мере я понимаю что происходит мало-мальски, а в твоём варианте всё просто дико не понятно, но понятно одно: интегрировать его в мой код- гигантская проблема(
Re: Зум
Добавлено: 26 июн 2013, 01:23
Banhammer
так от чего можно отталкиваться, чтобы не вылезала картинка из компоненты?