ایجاد امکان بزرگنمایی (zoom) دو انگشتی برای عکس نمایش داده شده در ImageView ، با توسعه عنصر ImageView ، در برنامه نویسی اندروید
در این مبحث، عنصر ImageView را توسعه (extend) می دهیم و یک عنصر جدید با نام TouchImageView می سازیم که ویزگی های ImageView را دارد و در عین حال، دارای قابلیت بزرگنمایی (zoom) دو انگشتی نیز می باشد.
در این پروژه اندروید، کدهای کلاس TouchImageView.java را بنده قبلا از یک سایت انگلیسی زبان گرفته بودم، اما متاسفانه آدرس آن را به خاطر ندارم که لینک منبع را ذکر کنم.
به عنوان یک مثال، پس از ساخت عنصر TouchImageView ، عکس زیر را در آن قرار می دهیم :
گوشی را می چرخانیم تا عکس، بهتر نمایش داده شود (بخش بیشتری از عنصر را بپوشاند) :
سپس با استفاده از قابلیت بزرگنمایی (zoom) دو انگشتی، بخشی از عکس را بزرگتر نمایش می دهیم :
پروژه اندروید را می توانید از انتهای این مبحث، دانلود نمایید.
در ادامه، بخش های مهم پروژه اندروید را شرح می دهیم.
در برنامه eclipse ، ابتدا یک پروژه اندروید با نام ImageViewZoom می سازیم (نام package را برابر com.kelidestan.imageviewzoom انتخاب می کنیم. نام Activity اصلی را برابر MainActivity انتخاب می کنیم و فایل xml متناظر آن را هم برابر activity_main قرار می دهیم).
یک عکس با نام image.jpg را در پوشه drawable-hdpi قرار می دهیم (همان عکسی که می خواهیم نمایش داده شود) :
همان طور که گفتیم، باید عنصر ImageView را توسعه (extend) بدهیم و یک عنصر جدید با نام دلخواه TouchImageView بسازیم. برای این منظور، یک کلاس (class) با نام TouchImageView بسازیم :
کدهای فایل TouchImageView.java را به صورت زیر می نویسیم :
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
Matrix matrix;
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
int viewWidth, viewHeight;
static final int CLICK = 3;
float saveScale = 1f;
protected float origWidth, origHeight;
int oldMeasuredWidth, oldMeasuredHeight;
ScaleGestureDetector mScaleDetector;
Context context;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
public void setMaxZoom(float x) {
maxScale = x;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
else
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
fixTrans();
return true;
}
}
void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
if (fixTransX != 0 || fixTransY != 0)
matrix.postTranslate(fixTransX, fixTransY);
}
float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
|| viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;
if (saveScale == 1) {
//Fit to screen.
float scale;
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();
Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
}
به این خط از کدها توجه کنید :
این خط از کدها بیان می کند که کلاس TouchImageView بر اساس توسعه (extend) کلاس ImageView (عنصر ImageView) ساخته شود.
چون یک عنصر جدید ساخته ایم، باید در فایل xml مربوط به Activity (یعنی فایل activity_main.xml)، عنصر TouchImageView را به جای عنصر ImageView به کار ببریم (در واقع، برای هر عکسی که می خواهیم قابلیت بزرگنمایی دو انگشتی داشته باشد) :
بنابراین، کدهای فایل activity_main.xml را به صورت زیر می نویسیم :
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.kelidestan.imageviewzoom.TouchImageView
android:id="@+id/touchImageView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
نکته مهم در کد بالا این است که ما به جای نام TouchImageView ، نام زیر را برای عنصر نوشته ایم :
که در آن، com.kelidestan.imageviewzoom برابر همان نام package برنامه اندروید ما می باشد، بنابراین دقت داشته باشید که در برنامه اندروید خود، ابتدا نام package برنامه خود را بنویسید و سپس نام TouchImageView را در انتهای آن قرار بدهید (اگر نام package را اشتباه بنویسید، یک خطا به وجود می آید و برنامه اندروید با Force Close روبرو می شود).
و در آخر، به سراغ فایل مربوط به Activity برنامه می رویم (یعنی فایل MainActivity.java) :
کدهای فایل MainActivity.java را به صورت زیر می نویسیم :
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TouchImageView img = (TouchImageView) findViewById(R.id.touchImageView);
img.setImageResource(R.drawable.image);
img.setMaxZoom(4f);
}
}
مشاهده می کنید که در هنگام تعریف عنصر TouchImageView در فایل java ، به صورت زیر عمل شده است :
فایل های پروژه اندروید را می توانید از لینک های زیر دانلود کنید :
با سلام خدمت شما
این کد هارو با اندکی تغییر داخل اندروید استادیو قرار دادم (مثلا پکیج رو تغیر دادم ) ولی به این دو خط ارور میده
توی مین اکتیویتی فقط یه ایمیج ویو هست و در
قسمت (
<com.application.smh.myapplication
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/touchImageView" />
)
تبدیل کردم
طبق چیزی که گفتید
ولی بازم این خط رو ارور میده
setContentView(R.layout.activity_main);
کد بالارو پاک کردم (
<com.application.smh.myapplication
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/touchImageView" />
)
و یه ایمیج ویو فقط گذاشتم با ای دی touchImageView و زمانی که برنامه رو اجرا میکنم این خط
touchImageView= (TouchImageView) findViewById(R.id.touchImageView);
ارور میگیره
لطفا راهنماییم کنید
با تشکر از سایت خوبتون که بیشتر کد هاش رو توی برنامه هام استفاده میکنم (البته از 5 تا یکیش اجرا میشه و کپی رایت رو هم رعایت میکنم )
سلام.
همان طور که در خود مبحث تذکر دادیم، باید عنصر سفارشی جدید که در برنامه اندروید ما طراحی شده است، با کدهای زیر در لایه گرافیکی (فایل xml) قرار داده شود :
در کد بالا، نام تگ را به صورت زیر نوشته ایم :
که در ابتدای آن، نام package برنامه اندروید (در اینجا برابر com.kelidestan.imageviewzoom) و سپس نام عنصر سفارشی (TouchImageView) ذکر شده است.
اکنون فرض کنیم که مثلا نام package برنامه اندروید شما برابر com.example.myapp باشد، آنگاه کدها به صورت زیر خواهند بود :
ما یک عنصر جدید با نام TouchImageView و بر اساس گسترش (extend) عنصر ImageView ساخته ایم، بنابراین دیگر نباید تگ ImageView به کار رود، بلکه نام تگ به همان شکل که شرح دادیم باید نوشته شود.
سلام دوست عزیز
راهنمایی تون میکنم جهت رفع ایراد این کد ها
اول اینکه این خط یعنی همون خط آخری که تو توضیح خوندید خطا داره و خطاش اینه که باید حرف تی انگلیسی رو با حروف بزبنویسید(قرمز کردمش راحت تر متوجه بشید)
TouchImageView img = (TouchImageView) findViewById(R.id.touchImageView)
دوم هم اینکه src این تاچ ایمیج ویوو رو که درست کردید آدرس اون عکسی که به برنامه ایکلیپس وارد کردید قرار بدبد
تو فایل ایکس ام ال این کد قرمز رو اضافه کنید
کد بخش قرمز رنگ :
با سلام و خسته نباشید ممنون از سایت خوبتان
میخواستم کمکم کنید اگر یک عکس از حافظه بخواهم حالت
زوم روش عمال بشه چه کدی باید به جای setImageRecoure جایگزین کرد
از setImageBitmap هم استفاده کردم جواب نمیدهد
ممنون میشم کمکم کنید
اگه امکانش هست جواب را برای من ایمیل هم بشه
سپاسگزارم
سلام توی این پروژه image view zoom کد ها و بدون خطا بود اما موقع اجرا عکس zoomنمیشه نه apk پروژه من نهapk پروژه ساخته شده شما لطفا بررسی کنید ممنون
فقط عکس رو نشون میده زوم نمیکنه
سلام.
شاید در برخی api ها کد به درستی عمل نمی کند.
قبلا یکی از کاربران در انجمن تذکر دادند که کدهای این کلید در برخی api ها (نسخه های اندروید) عمل نمی کند، اما بنده این مورد را شخصا بررسی نکردم.
بنابراین می توانید آن را بر روی شبیه سازهایی با api های متفاوت تست کنید و ببینید مشکل از تفاوت api است یا خیر و آیا می توان با تغییر برخی کلاس ها (classes) و روش های (methods) به کار رفته در آن، کد را برای تمامی api ها به کار برد یا امکان آن وجود ندارد (در محیط برنامه نویسی، جدیدترین نسخه اندروید را به کار ببرید تا اگر کلاس یا روشی قدیمی شده است، خود محیط برنامه نویسی تذکر بدهد و بتوانید کلاس ها و روش های جایگزین را بیابید).
دوست عزیزی که می فرمایید زوم نمیشه
ببینید کجای کداتون اشتباه نوشتین
بنده که این کد ها رو کپی کردم بدون تغییرات اجرا میشن اونم بدون مشکل
هیچی هم تغییر ندادم
سلام خسته نباشید. چطورمیشه روی تصاویر زوم شده اسکرول کنیم ؟ چون بعد از زوم قسمتهای دیگه تصویر ازبین میروند.میشه یک نمونه آموزش بفرمایید که هم ذوی تصویر زوم کنه و هم تصویر زوم شده رو بشه اسکرول کرد که به همه جای تصویر بعد از زوم و کنار زدن دسنرسی داشته باشیم . مثه خود گالری اندروید.
اگر کار دشواریست میشه بجاش یک قاب تعریف کنیم وعکسمونو با سایز اصلی درون قاب بذاریم و از درون قاب به راحتی اسکرول کنیم؟ اگر میشه چطور؟ مثلا مثه همین کاری که شما برای کدها انجام دادید. دز صفحه اصلی یه منطقه کوچیکی برای کدها اختصاص داده شده وهمه کدها تو اون نوشته شدن و اسکرول میشن اونجوری فضای کمی هم از صفحه پر میشه. میشه ازینا اونی که آسون ترهست رو آموزش بدید ممنون از سایت خوبتون
سلام.
یک راه حل ساده برای نمایش عکس با قابلیت اسکرول به بالا و پایین، استفاده از ScrollView می باشد :
یعنی یک تگ ImageView در تگ ScrollView قرار می دهید که عکس در آن نمایش داده می شود. ارتفاع ScrollView را برابر یک مقدار مشخص تعیین می کنید (مثلا بر حسب dp). بنابراین اگر ارتفاع عکس از ارتفاع ScrollView بیشتر باشد، قابلیت اسکرول به بالا و پایین وجود خواهد داشت.
علاوه بر این، توصیه می کنم در اینترنت جستجو کنید، زیرا عناصر جدیدی مربوط به اسکرول نیز ارائه شده است و همچنین ممکن است کتابخانه های (libraries) مناسبی را در این زمینه بیابید.
سلام جناب admin
من کد zoom رو استفاده کردم و هیچ مشکلی هم نداره.
فقط یه مشکل دارم؛
میخوام وقتی روی دکمه Next Phicture پروژه ام کلیک میکنم، عکس بعدی که میخواد نشون بده zoom شده نباشه!
در واقع چیزی که نیاز دارم اینه:
دستور این که عکسی که در TouchImageView وجود داره و zoom شده به حالت اول دربیاد؟
سلام خدمت دوستان
واقعا عالی بود این پست شما،من اخیرا با سایت شما آشنا شدم و خیلی مطالب خوبی دارید حتما بیشتر از اینا استفاده میکنم.
فقط یه سوالی، من وقتی پروژه رو ران میکنم اخطار has stop میده وخارج میشه.مشکل از کجا میتونه باشه؟(تو گوشی هم تست کردم همین اخطارو میده)