summaryrefslogtreecommitdiff
path: root/org.fox.ttrss/src/main/java/org/fox/ttrss/util/EnlargingImageView.java
blob: bf9d48d8cde8cea96d1eb4e212a942380171505d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
package org.fox.ttrss.util;

/*
 * Copyright (C) 2013 Tomáš Procházka
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.lang.reflect.Field;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * Special version of ImageView which allow enlarge width of image if android:adjustViewBounds is true.
 *
 * <p>It simulate HTML behaviour &lt;img src="" widh="100" /&gt;</p>
 * <p><a href="http://stackoverflow.com/questions/6202000/imageview-one-dimension-to-fit-free-space-and-second-evaluate-to-keep-aspect-rati">Stackoverflow question link</a></p>
 *
 * <p>It also allow set related view which will be used as reference for size measure.</p>
 *
 * @author Tomáš Procházka &lt;<a href="mailto:[email protected]">[email protected]</a>&gt;
 * @version $Revision: 0$ ($Date: 6.6.2011 18:16:52$)
 */
public class EnlargingImageView extends android.widget.ImageView {

	private int mDrawableWidth;
	private int mDrawableHeight;
	private boolean mAdjustViewBoundsL;
	private int mMaxWidthL = Integer.MAX_VALUE;
	private int mMaxHeightL = Integer.MAX_VALUE;
	private View relatedView;

	public EnlargingImageView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		// hack for acces some private field of parent :-(
		Field f;
		try {
			f = android.widget.ImageView.class.getDeclaredField("mAdjustViewBounds");
			f.setAccessible(true);
			setAdjustViewBounds((Boolean) f.get(this));

			f = android.widget.ImageView.class.getDeclaredField("mMaxWidth");
			f.setAccessible(true);
			setMaxWidth((Integer) f.get(this));

			f = android.widget.ImageView.class.getDeclaredField("mMaxHeight");
			f.setAccessible(true);
			setMaxHeight((Integer) f.get(this));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public EnlargingImageView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public EnlargingImageView(Context context) {
		super(context);
	}

	public void setAdjustViewBounds(boolean adjustViewBounds) {
		super.setAdjustViewBounds(adjustViewBounds);
		mAdjustViewBoundsL = adjustViewBounds;
	}

	public void setMaxWidth(int maxWidth) {
		super.setMaxWidth(maxWidth);
		mMaxWidthL = maxWidth;
	}

	public void setMaxHeight(int maxHeight) {
		super.setMaxHeight(maxHeight);
		mMaxHeightL = maxHeight;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		if (getDrawable() == null) {
			setMeasuredDimension(0, 0);
			return;
		}

		mDrawableWidth = getDrawable().getIntrinsicWidth();
		mDrawableHeight = getDrawable().getIntrinsicHeight();

		int w = 0;
		int h = 0;

		// Desired aspect ratio of the view's contents (not including padding)
		float desiredAspect = 0.0f;

		// We are allowed to change the view's width
		boolean resizeWidth = false;

		// We are allowed to change the view's height
		boolean resizeHeight = false;

		if (mDrawableWidth > 0) {
			w = mDrawableWidth;
			h = mDrawableHeight;
			if (w <= 0) w = 1;
			if (h <= 0) h = 1;

			// We are supposed to adjust view bounds to match the aspect
			// ratio of our drawable. See if that is possible.
			if (mAdjustViewBoundsL) {

				int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
				int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

				resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
				resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

				desiredAspect = (float) w / (float) h;
			}
		}

		int pleft = getPaddingLeft();
		int pright = getPaddingRight();
		int ptop = getPaddingTop();
		int pbottom = getPaddingBottom();

		int widthSize;
		int heightSize;

		if (resizeWidth || resizeHeight) {
			/* If we get here, it means we want to resize to match the
			    drawables aspect ratio, and we have the freedom to change at
			    least one dimension.
			*/

			// Get the max possible width given our constraints
			widthSize = resolveAdjustedSize(w + pleft + pright,
				mMaxWidthL, widthMeasureSpec);

			// Get the max possible height given our constraints
			heightSize = resolveAdjustedSize(h + ptop + pbottom,
				mMaxHeightL, heightMeasureSpec);

			if (desiredAspect != 0.0f) {
				// See what our actual aspect ratio is
				float actualAspect = (float) (widthSize - pleft - pright) /
					(heightSize - ptop - pbottom);

				if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {

					// Try adjusting width to be proportional to height
					if (resizeWidth) {
						int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright;

                        if (newWidth > 0 && widthSize > 0 && newWidth / widthSize > 2)
                            newWidth = widthSize * 2;

						if (/*newWidth <= widthSize &&*/ newWidth > 0) {
							widthSize = Math.min(newWidth, mMaxWidthL);
							heightSize = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom;
						}
					}

					// Try adjusting height to be proportional to width
					if (resizeHeight) {
						int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom;

                        if (newHeight > 0 && heightSize > 0 && newHeight / heightSize > 2)
                            newHeight = heightSize * 2;

						if (/* newHeight <= heightSize && */ newHeight > 0) {
							heightSize = Math.min(newHeight, mMaxHeightL);
							widthSize = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright;
						}
					}
				}
			}
		} else {
			/* We are either don't want to preserve the drawables aspect ratio,
			   or we are not allowed to change view dimensions. Just measure in
			   the normal way.
			*/
			w += pleft + pright;
			h += ptop + pbottom;

			w = Math.max(w, getSuggestedMinimumWidth());
			h = Math.max(h, getSuggestedMinimumHeight());

			widthSize = resolveSize(w, widthMeasureSpec);
			heightSize = resolveSize(h, heightMeasureSpec);
		}

		//Log.d(Constants.LOGTAG, mDrawableWidth + ":" +  mDrawableHeight + " to " + widthSize + ":" + heightSize);

		setMeasuredDimension(widthSize, heightSize);

		if (relatedView != null) {
			//Log.i(Constants.LOGTAG, getTag() +  " onMeasure:" +  widthSize + ", " + heightSize + " update size of related view!");
			relatedView.getLayoutParams().width = widthSize;
			relatedView.getLayoutParams().height = heightSize;
		}

	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		//Log.d(Constants.LOGTAG, getTag() +  " onLayout:" +  left + ", " + top + ", " + right + ", " + bottom);
	}

	/**
	 * Experimental. This view will be set to the same size as this image.
	 */
	public void setRelatedView(View view) {
		relatedView = view;
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		//Log.d(Constants.LOGTAG, getTag() + " onSizeChanged:" +  w + ", " + h + ", " + oldw + ", " + oldh);
	}

	private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) {
		int result = desiredSize;
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);
		switch (specMode) {
		case MeasureSpec.UNSPECIFIED:
			/* Parent says we can be as big as we want. Just don't be larger
			than max size imposed on ourselves.
			*/
			result = Math.min(desiredSize, maxSize);
			break;
		case MeasureSpec.AT_MOST:
			// Parent says we can be as big as we want, up to specSize.
			// Don't be larger than specSize, and don't be larger than
			// the max size imposed on ourselves.
			result = Math.min(Math.min(desiredSize, specSize), maxSize);
			break;
		case MeasureSpec.EXACTLY:
			// No choice. Do what we are told.
			result = specSize;
			break;
		}
		return result;
	}
}