안 드 로 이 드 폭포 흐름 사진 벽 불규칙 배열 의 아름다움 체험
제 가 예전 에 안 드 로 이 드 에서 포 토 월 기능 을 어떻게 실현 하 는 지 에 관 한 글 을 썼 던 기억 이 있 습 니 다.그런데 그 때 는 GridView 를 사용 하여 레이아웃 을 했 습 니 다.이런 레이아웃 방식 은'벽'에 있 는 모든 그림 의 크기 가 똑 같은 경우 에 만 적 용 됩 니 다.그림 의 크기 가 들쭉날쭉 하면 GridView 에 나타 나 면 매우 보기 싫 습 니 다.폭포 류 의 배치 방식 을 사용 하면 이 문 제 를 잘 해결 할 수 있 기 때문에 오늘 도 트 렌 드 를 따라 잡 아 안 드 로 이 드 에서 폭포 류 사진 벽의 기능 을 어떻게 실현 하 는 지 살 펴 보 자.
우선 실현 원 리 를 말 해 보 자.폭포 류 의 구조 방식 은 자 유 롭 게 배열 되 어 있 는 것 처럼 보이 지만 사실은 과학적 인 배열 규칙 이 있다.전체 화면 은 화면의 너비 에 따라 등 폭 의 몇 열 로 나 눌 것 이다.핸드폰 의 화면 이 크 지 않 기 때문에 여기 서 우 리 는 세 열 로 나 눌 것 이다.그림 을 추가 해 야 할 때마다 이 그림 의 너 비 를 열 과 같은 너비 로 압축 한 다음 에 같은 압축 비율 로 그림 의 높이 를 압축 한 다음 에 이 세 열 에서 현재 높이 가 가장 작은 열 을 찾 아 그림 을 이 열 에 추가 합 니 다.이후 새로운 그림 을 추가 해 야 할 때마다 위의 조작 을 반복 하면 폭포 흐름 구조의 사진 벽 이 형성 된다.설명 도 는 다음 과 같다.
내 가 이렇게 말 한 것 을 들 으 면 폭포 흐름 의 구조 가 매우 간단 하 다 고 느 낄 수 있 습 니 다.세 개의 LinearLayout 를 사용 하여 전체 화면 폭 을 평평 하 게 나 눈 다음 에 동적 으로 addView()를 들 어가 면 됩 니 다.그 렇 긴 하 다.단지 기능 을 실현 하기 위해 서 라면 이렇게 간단 하 다.하지만 우 리 는 휴대 전화 개발 을 하고 있다 는 것 을 잊 지 마 세 요.리 니 어 라 이 엇 에 그림 을 계속 추가 하면 프로그램 이 곧 OOM 이 될 것 입 니 다.따라서 우 리 는 이미지 자원 을 방출 하 는 합 리 적 인 방안 이 필요 하 다.여 기 는 아직도 LruCache 알고리즘 을 사용 하려 고 하 는데 이 알고리즘 에 익숙 하지 않 은 친 구 는 Android 고 효율 로드 큰 그림,다 중 그림 방안,프로그램 OOM 을 효과적으로 피 할 수 있 습 니 다. 을 참고 할 수 있다.
다음은 안 드 로 이 드 프로젝트 를 새로 만 들 고 PhotoWallFalls Demo 라 는 이름 을 짓 고 4.0 API 를 선택 하 겠 습 니 다.
첫 번 째 로 고려 해 야 할 문 제 는 크기 가 들쭉날쭉 한 그림 들 을 어디서 수집 하 느 냐 는 것 이다.여기 서 저 는 사전에 바 이 두 에서 풍경 사진 을 많이 검색 하고 그들의 방문 안정성 을 확보 하기 위해 이 사진 들 을 제 CSDN 앨범 에 올 렸 습 니 다.그래서 여기 서 사진 을 다운로드 하면 됩 니 다.그림%1 개의 캡 션 을 편 집 했 습 니 다.
public class Images {
public final static String[] imageUrls = new String[] {
"http://img.my.csdn.net/uploads/201309/01/1378037235_3453.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037235_9280.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037234_3539.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037234_6318.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037194_2965.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037193_1687.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037193_1286.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037192_8379.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037178_9374.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037177_1254.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037177_6203.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037152_6352.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037151_9565.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037151_7904.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037148_7104.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037129_8825.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037128_5291.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037128_3531.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037127_1085.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037095_7515.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037094_8001.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037093_7168.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037091_4950.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949643_6410.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949642_6939.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949630_4505.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949630_4593.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949629_7309.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949629_8247.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949615_1986.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_8482.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_3743.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_4199.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949599_3416.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949599_5269.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949598_7858.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949598_9982.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949578_2770.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949578_8744.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949577_5210.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949577_1998.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949482_8813.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949481_6577.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949480_4490.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949455_6792.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949455_6345.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949442_4553.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949441_8987.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949441_5454.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949454_6367.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949442_4562.jpg" };
}
그 다음 에 이미지 Loader 류 를 새로 만 들 면 그림 을 관리 하 는 데 편리 합 니 다.코드 는 다음 과 같 습 니 다.
public class ImageLoader {
/**
* , , 。
*/
private static LruCache<String, Bitmap> mMemoryCache;
/**
* ImageLoader 。
*/
private static ImageLoader mImageLoader;
private ImageLoader() {
//
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
// 1/8
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
}
/**
* ImageLoader 。
*
* @return ImageLoader 。
*/
public static ImageLoader getInstance() {
if (mImageLoader == null) {
mImageLoader = new ImageLoader();
}
return mImageLoader;
}
/**
* LruCache 。
*
* @param key
* LruCache , URL 。
* @param bitmap
* LruCache , Bitmap 。
*/
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemoryCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
/**
* LruCache , null。
*
* @param key
* LruCache , URL 。
* @return Bitmap , null。
*/
public Bitmap getBitmapFromMemoryCache(String key) {
return mMemoryCache.get(key);
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth) {
//
final int width = options.outWidth;
int inSampleSize = 1;
if (width > reqWidth) {
//
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth) {
// inJustDecodeBounds true,
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth);
// inSampleSize
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, options);
}
}
여기에서 ImageLoader 클래스 를 하나의 예 로 설정 하고 구조 함수 에서 LruCache 클래스 를 초기 화 하 며 최대 캐 시 용량 을 최대 사용 가능 한 메모리 의 1/8 로 설정 합 니 다.그리고 LruCache 를 조작 하고 그림 을 압축 하고 읽 을 수 있 는 다른 몇 가지 방법 을 제공 했다.다음 에 새로 만 든 MyScrollView 는 ScrollView 에서 계승 합 니 다.코드 는 다음 과 같 습 니 다.
public class MyScrollView extends ScrollView implements OnTouchListener {
/**
*
*/
public static final int PAGE_SIZE = 15;
/**
*
*/
private int page;
/**
*
*/
private int columnWidth;
/**
*
*/
private int firstColumnHeight;
/**
*
*/
private int secondColumnHeight;
/**
*
*/
private int thirdColumnHeight;
/**
* layout, onLayout
*/
private boolean loadOnce;
/**
*
*/
private ImageLoader imageLoader;
/**
*
*/
private LinearLayout firstColumn;
/**
*
*/
private LinearLayout secondColumn;
/**
*
*/
private LinearLayout thirdColumn;
/**
* 。
*/
private static Set<LoadImageTask> taskCollection;
/**
* MyScrollView 。
*/
private static View scrollLayout;
/**
* MyScrollView 。
*/
private static int scrollViewHeight;
/**
* 。
*/
private static int lastScrollY = -1;
/**
* , 。
*/
private List<ImageView> imageViewList = new ArrayList<ImageView>();
/**
* Handler , 。
*/
private static Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
MyScrollView myScrollView = (MyScrollView) msg.obj;
int scrollY = myScrollView.getScrollY();
// ,
if (scrollY == lastScrollY) {
// , ,
if (scrollViewHeight + scrollY >= scrollLayout.getHeight()
&& taskCollection.isEmpty()) {
myScrollView.loadMoreImages();
}
myScrollView.checkVisibility();
} else {
lastScrollY = scrollY;
Message message = new Message();
message.obj = myScrollView;
// 5
handler.sendMessageDelayed(message, 5);
}
};
};
/**
* MyScrollView 。
*
* @param context
* @param attrs
*/
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
imageLoader = ImageLoader.getInstance();
taskCollection = new HashSet<LoadImageTask>();
setOnTouchListener(this);
}
/**
* , MyScrollView , 。 。
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed && !loadOnce) {
scrollViewHeight = getHeight();
scrollLayout = getChildAt(0);
firstColumn = (LinearLayout) findViewById(R.id.first_column);
secondColumn = (LinearLayout) findViewById(R.id.second_column);
thirdColumn = (LinearLayout) findViewById(R.id.third_column);
columnWidth = firstColumn.getWidth();
loadOnce = true;
loadMoreImages();
}
}
/**
* , 。
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
Message message = new Message();
message.obj = this;
handler.sendMessageDelayed(message, 5);
}
return false;
}
/**
* , 。
*/
public void loadMoreImages() {
if (hasSDCard()) {
int startIndex = page * PAGE_SIZE;
int endIndex = page * PAGE_SIZE + PAGE_SIZE;
if (startIndex < Images.imageUrls.length) {
Toast.makeText(getContext(), " ...", Toast.LENGTH_SHORT)
.show();
if (endIndex > Images.imageUrls.length) {
endIndex = Images.imageUrls.length;
}
for (int i = startIndex; i < endIndex; i++) {
LoadImageTask task = new LoadImageTask();
taskCollection.add(task);
task.execute(Images.imageUrls[i]);
}
page++;
} else {
Toast.makeText(getContext(), " ", Toast.LENGTH_SHORT)
.show();
}
} else {
Toast.makeText(getContext(), " SD ", Toast.LENGTH_SHORT).show();
}
}
/**
* imageViewList , , , 。
*/
public void checkVisibility() {
for (int i = 0; i < imageViewList.size(); i++) {
ImageView imageView = imageViewList.get(i);
int borderTop = (Integer) imageView.getTag(R.string.border_top);
int borderBottom = (Integer) imageView
.getTag(R.string.border_bottom);
if (borderBottom > getScrollY()
&& borderTop < getScrollY() + scrollViewHeight) {
String imageUrl = (String) imageView.getTag(R.string.image_url);
Bitmap bitmap = imageLoader.getBitmapFromMemoryCache(imageUrl);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
LoadImageTask task = new LoadImageTask(imageView);
task.execute(imageUrl);
}
} else {
imageView.setImageResource(R.drawable.empty_photo);
}
}
}
/**
* SD 。
*
* @return SD true, false。
*/
private boolean hasSDCard() {
return Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState());
}
/**
* 。
*
* @author guolin
*/
class LoadImageTask extends AsyncTask<String, Void, Bitmap> {
/**
* URL
*/
private String mImageUrl;
/**
* ImageView
*/
private ImageView mImageView;
public LoadImageTask() {
}
/**
* ImageView
*
* @param imageView
*/
public LoadImageTask(ImageView imageView) {
mImageView = imageView;
}
@Override
protected Bitmap doInBackground(String... params) {
mImageUrl = params[0];
Bitmap imageBitmap = imageLoader
.getBitmapFromMemoryCache(mImageUrl);
if (imageBitmap == null) {
imageBitmap = loadImage(mImageUrl);
}
return imageBitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
double ratio = bitmap.getWidth() / (columnWidth * 1.0);
int scaledHeight = (int) (bitmap.getHeight() / ratio);
addImage(bitmap, columnWidth, scaledHeight);
}
taskCollection.remove(this);
}
/**
* URL, 。 SD , SD , 。
*
* @param imageUrl
* URL
* @return 。
*/
private Bitmap loadImage(String imageUrl) {
File imageFile = new File(getImagePath(imageUrl));
if (!imageFile.exists()) {
downloadImage(imageUrl);
}
if (imageUrl != null) {
Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(
imageFile.getPath(), columnWidth);
if (bitmap != null) {
imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
return bitmap;
}
}
return null;
}
/**
* ImageView
*
* @param bitmap
*
* @param imageWidth
*
* @param imageHeight
*
*/
private void addImage(Bitmap bitmap, int imageWidth, int imageHeight) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
imageWidth, imageHeight);
if (mImageView != null) {
mImageView.setImageBitmap(bitmap);
} else {
ImageView imageView = new ImageView(getContext());
imageView.setLayoutParams(params);
imageView.setImageBitmap(bitmap);
imageView.setScaleType(ScaleType.FIT_XY);
imageView.setPadding(5, 5, 5, 5);
imageView.setTag(R.string.image_url, mImageUrl);
findColumnToAdd(imageView, imageHeight).addView(imageView);
imageViewList.add(imageView);
}
}
/**
* 。 , 。
*
* @param imageView
* @param imageHeight
* @return
*/
private LinearLayout findColumnToAdd(ImageView imageView,
int imageHeight) {
if (firstColumnHeight <= secondColumnHeight) {
if (firstColumnHeight <= thirdColumnHeight) {
imageView.setTag(R.string.border_top, firstColumnHeight);
firstColumnHeight += imageHeight;
imageView.setTag(R.string.border_bottom, firstColumnHeight);
return firstColumn;
}
imageView.setTag(R.string.border_top, thirdColumnHeight);
thirdColumnHeight += imageHeight;
imageView.setTag(R.string.border_bottom, thirdColumnHeight);
return thirdColumn;
} else {
if (secondColumnHeight <= thirdColumnHeight) {
imageView.setTag(R.string.border_top, secondColumnHeight);
secondColumnHeight += imageHeight;
imageView
.setTag(R.string.border_bottom, secondColumnHeight);
return secondColumn;
}
imageView.setTag(R.string.border_top, thirdColumnHeight);
thirdColumnHeight += imageHeight;
imageView.setTag(R.string.border_bottom, thirdColumnHeight);
return thirdColumn;
}
}
/**
* SD 。
*
* @param imageUrl
* URL 。
*/
private void downloadImage(String imageUrl) {
HttpURLConnection con = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
File imageFile = null;
try {
URL url = new URL(imageUrl);
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5 * 1000);
con.setReadTimeout(15 * 1000);
con.setDoInput(true);
con.setDoOutput(true);
bis = new BufferedInputStream(con.getInputStream());
imageFile = new File(getImagePath(imageUrl));
fos = new FileOutputStream(imageFile);
bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024];
int length;
while ((length = bis.read(b)) != -1) {
bos.write(b, 0, length);
bos.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (bos != null) {
bos.close();
}
if (con != null) {
con.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (imageFile != null) {
Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(
imageFile.getPath(), columnWidth);
if (bitmap != null) {
imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
}
}
}
/**
* 。
*
* @param imageUrl
* URL 。
* @return 。
*/
private String getImagePath(String imageUrl) {
int lastSlashIndex = imageUrl.lastIndexOf("/");
String imageName = imageUrl.substring(lastSlashIndex + 1);
String imageDir = Environment.getExternalStorageDirectory()
.getPath() + "/PhotoWallFalls/";
File file = new File(imageDir);
if (!file.exists()) {
file.mkdirs();
}
String imagePath = imageDir + imageName;
return imagePath;
}
}
}
마 이 스크롤 뷰 는 폭포 흐름 을 실현 하 는 포 토 월 의 핵심 클래스 로,제 가 중점적으로 소개 해 드 리 겠 습 니 다.우선 스크롤 뷰 에서 계승 한 것 으로 스크롤 방식 으로 더 많은 그림 을 볼 수 있 습 니 다.다음 페이지 의 그림 을 불 러 오 는 데 사용 되 는 loadmore Images()방법 을 제공 합 니 다.따라서 onLayout()방법 에서 첫 페이지 의 그림 을 초기 화하 기 위해 서 이 방법 을 한 번 호출 해 야 합 니 다.그리고 onTouch 방법 에 서 는 손가락 이 화면 을 떠 나 는 이 벤트 를 들 을 때마다 handler 를 통 해 현재 ScrollView 의 스크롤 상 태 를 판단 합 니 다.맨 밑 으로 굴 러 간 것 을 발견 하면 다음 페이지 의 그림 을 다시 불 러 옵 니 다.그럼 로드 모어 이미지()방법의 내부 디 테 일 을 살 펴 보 겠 습 니 다.이 방법 에 서 는 이 페이지 의 모든 그림 을 불 러 오 는 순환 을 사용 합 니 다.매번 LoadImageTask 를 열 어 그림 을 비동기 로 불 러 옵 니 다.그리고 LoadImageTask 에 서 는 먼저 이 그림 이 SD 카드 에 이미 존재 하 는 지 확인 하고,아직 존재 하지 않 는 다 면 인터넷 에서 다운로드 한 다음 이 그림 을 LruCache 에 저장 합 니 다.이 어 이 그림 을 일정한 비율 로 압축 하고 현재 높이 가 가장 작은 열 을 찾 아 압축 된 그림 을 추가 하면 된다.
또 사진 벽 에 있 는 그림 이 모두 적절하게 회 수 될 수 있 도록 체크 비 전 빌 리 티(checkVisibility)방법 을 추가 했다.이 방법의 핵심 사상 은 현재 사진 벽 에 있 는 모든 그림 을 검사 하고 어떤 것 이 보이 고 어떤 것 이 보이 지 않 는 지 판단 하 는 것 이다.그리고 보이 지 않 는 그림 들 을 모두 빈 그림 으로 바 꾸 면 프로그램 이 항상 높 은 메모 리 를 차지 하지 않도록 할 수 있다.이 그림 들 이 다시 보 이 는 것 으로 바 뀌 었 을 때 는 LruCache 에서 이 그림 들 을 다시 꺼 내 면 된다.만약 어떤 그림 이 LruCache 에서 제거 되 었 다 면 LoadImageTask 를 열 어 이 그림 을 메모리 에 다시 불 러 옵 니 다.
그리고 액 티 비 티 를 열거 나 새로 만 듭 니 다main.xml,그 안에 폭포 흐름 의 배치 방식 을 설정 합 니 다.다음 과 같 습 니 다.
<com.example.photowallfallsdemo.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/my_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/first_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/second_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/third_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
</LinearLayout>
</LinearLayout>
</com.example.photowallfallsdemo.MyScrollView>
이 를 통 해 알 수 있 듯 이 우 리 는 방금 작 성 된 MyScrollView 를 루트 레이아웃 으로 사용 한 다음 에 그 안에 직접 하위 레이아웃 LinearLayout 를 넣 어 현재 미끄럼 구조의 높이 를 통계 한 다음 에 이 레이아웃 에 세 개의 등 폭 LinearLayout 를 각각 1 열,2 열,3 열 로 추가 했다.이렇게 하면 MyScrollView 에서 이 세 개의 LinearLayout 에 그림 을 동적 으로 추가 할 수 있 습 니 다.마지막 으로 네트워크 와 SD 카드 에 저 장 된 기능 을 사 용 했 기 때문에 AndroidManifest.xml 에 다음 과 같은 권한 을 추가 해 야 합 니 다.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
이렇게 하면 우리 의 모든 인 코딩 작업 이 이미 완성 되 었 습 니 다.지금 은 실행 을 시도 해 볼 수 있 습 니 다.효 과 는 다음 그림 과 같 습 니 다.폭포 흐름 모드 의 사진 벽 과 는 정말 아름 답 습 니 다.그리고 우 리 는 매우 완벽 한 자원 방출 체 제 를 가지 기 때문에 사진 벽 에 얼마나 많은 그림 을 추가 하 든 프로그램 이 메모 리 를 차지 하 는 것 은 합 리 적 인 범위 내 에서 유 지 됩 니 다.다음 글 에서 저 는 여러분 을 데 리 고 이 프로그램 을 계속 보완 할 것 입 니 다.클릭 하여 큰 그림 을 보고 여러 개의 터치 로 크기 를 조정 하 는 기능 을 추가 할 것 입 니 다.관심 이 있 는 분 들 은 Android 멀 티 터치 기술 실전,자 유 롭 게 그림 크기 조정 및 이동 을 계속 읽 으 세 요.
원본 다운로드:http://xiazai.jb51.net/201610/yuanma/AndroidPhotoWallFalls(jb51.net).rar
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.