Android实现图片滚动控件

Android实现图片滚动控件
Android实现图片滚动控件

实现原理其实还是之前那篇文章Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现,算是以那个原理为基础的另外一个变种。正所谓一通百通,真正掌握一种方法之后,就可以使用这个方法变换出各种不通的效果。

今天仍然还是实现一个自定义控件,然后我们在任意Activity的布局文件中引用一下,即可实现图片滚动器的效果。

在Eclipse中新建一个Android项目,项目名就叫做SlidingViewSwitcher。

新建一个类,名叫SlidingSwitcherView,这个类是继承自RelativeLayout的,并且实现了OnTouchListener接口,具体代码如下:

[java]view plaincopy

1.public class SlidingSwitcherView extends RelativeLayout i

mplements OnTouchListener {

2.

3./**

4. * 让菜单滚动,手指滑动需要达到的速度。

5. */

6.public static final int SNAP_VELOCITY = 200;

7.

8./**

9. * SlidingSwitcherView的宽度。

10. */

11.private int switcherViewWidth;

12.

13./**

14. * 当前显示的元素的下标。

15. */

16.private int currentItemIndex;

17.

18./**

19. * 菜单中包含的元素总数。

20. */

21.private int itemsCount;

22.

23./**

24. * 各个元素的偏移边界值。

25. */

26.private int[] borders;

27.

28./**

29. * 最多可以滑动到的左边缘。值由菜单中包含的元素总数来定,

marginLeft到达此值之后,不能再减少。

30. *

31. */

32.private int leftEdge = 0;

33.

34./**

35. * 最多可以滑动到的右边缘。值恒为0,marginLeft到达此值之

后,不能再增加。

36. */

37.private int rightEdge = 0;

38.

39./**

40. * 记录手指按下时的横坐标。

41. */

42.private float xDown;

43.

44./**

45. * 记录手指移动时的横坐标。

46. */

47.private float xMove;

48.

49./**

50. * 记录手机抬起时的横坐标。

51. */

52.private float xUp;

53.

54./**

55. * 菜单布局。

56. */

57.private LinearLayout itemsLayout;

58.

59./**

60. * 标签布局。

61. */

62.private LinearLayout dotsLayout;

63.

64./**

65. * 菜单中的第一个元素。

66. */

67.private View firstItem;

68.

69./**

70. * 菜单中第一个元素的布局,用于改变leftMargin的值,来决

定当前显示的哪一个元素。

71. */

72.private MarginLayoutParams firstItemParams;

73.

74./**

75. * 用于计算手指滑动的速度。

76. */

77.private VelocityTracker mVelocityTracker;

78.

79./**

80. * 重写SlidingSwitcherView的构造函数,用于允许在XML中

引用当前的自定义布局。

81. *

82. * @param context

83. * @param attrs

84. */

85.public SlidingSwitcherView(Context context, Attribut

eSet attrs) {

86.super(context, attrs);

87. }

88.

89./**

90. * 滚动到下一个元素。

91. */

92.public void scrollToNext() {

93.new ScrollTask().execute(-20);

94. }

95.

96./**

97. * 滚动到上一个元素。

98. */

99.public void scrollToPrevious() {

100.new ScrollTask().execute(20);

101. }

102.

103./**

104. * 在onLayout中重新设定菜单元素和标签元素的参数。105. */

106.@Override

107.protected void onLayout(boolean changed, int l, i nt t, int r, int b) {

108.super.onLayout(changed, l, t, r, b);

109.if (changed) {

110. initializeItems();

111. initializeDots();

112. }

113. }

114.

115./**

116. * 初始化菜单元素,为每一个子元素增加监听事件,并且改变所有子元素的宽度,让它们等于父元素的宽度。

117. */

118.private void initializeItems() {

119. switcherViewWidth = getWidth();

120. itemsLayout = (LinearLayout) getChildAt(0); 121. itemsCount = itemsLayout.getChildCount(); 122. borders = new int[itemsCount];

123.for (int i = 0; i < itemsCount; i++) { 124. borders[i] = -i * switcherViewWidth; 125. View item = itemsLayout.getChildAt(i); 126. MarginLayoutParams params = (MarginLayout Params) item.getLayoutParams();

127. params.width = switcherViewWidth;

128. item.setLayoutParams(params);

129. item.setOnTouchListener(this);

130. }

131. leftEdge = borders[itemsCount - 1];

132. firstItem = itemsLayout.getChildAt(0); 133. firstItemParams = (MarginLayoutParams) firstI tem.getLayoutParams();

134. }

135.

136./**

137. * 初始化标签元素。

138. */

139.private void initializeDots() {

140. dotsLayout = (LinearLayout) getChildAt(1); 141. refreshDotsLayout();

142. }

143.

144.@Override

145.public boolean onTouch(View v, MotionEvent event) {

146. createVelocityTracker(event);

147.switch (event.getAction()) {

148.case MotionEvent.ACTION_DOWN:

149.// 手指按下时,记录按下时的横坐标

150. xDown = event.getRawX();

151.break;

152.case MotionEvent.ACTION_MOVE:

153.// 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整左侧布局的leftMargin值,从而显示和隐藏左侧布局154. xMove = event.getRawX();

155.int distanceX = (int) (xMove - xDown) - ( currentItemIndex * switcherViewWidth);

156. firstItemParams.leftMargin = distanceX; 157.if (beAbleToScroll()) {

158. firstItem.setLayoutParams(firstItemPa rams);

159. }

160.break;

161.case MotionEvent.ACTION_UP:

162.// 手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局

163. xUp = event.getRawX();

164.if (beAbleToScroll()) {

165.if (wantScrollToPrevious()) {

166.if (shouldScrollToPrevious()) { 167. currentItemIndex--;

168. scrollToPrevious();

169. refreshDotsLayout();

170. } else {

171. scrollToNext();

172. }

173. } else if (wantScrollToNext()) { 174.if (shouldScrollToNext()) { 175. currentItemIndex++;

176. scrollToNext();

177. refreshDotsLayout();

178. } else {

179. scrollToPrevious();

180. }

181. }

182. }

183. recycleVelocityTracker();

184.break;

185. }

186.return false;

187. }

188.

189./**

190. * 当前是否能够滚动,滚动到第一个或最后一个元素时将不能再滚动。

191. *

192. * @return 当前leftMargin的值在leftEdge和rightEdge之间返回true,否则返回false。

193. */

194.private boolean beAbleToScroll() {

195.return firstItemParams.leftMargin < rightEdge && firstItemParams.leftMargin > leftEdge;

196. }

197.

198./**

199. * 判断当前手势的意图是不是想滚动到上一个菜单元素。如果手指移动的距离是正数,则认为当前手势是想要滚动到上一个菜单元素。200. *

201. * @return 当前手势想滚动到上一个菜单元素返回true,否则返回false。

202. */

203.private boolean wantScrollToPrevious() {

204.return xUp - xDown > 0;

205. }

206.

207./**

208. * 判断当前手势的意图是不是想滚动到下一个菜单元素。如果手指移动的距离是负数,则认为当前手势是想要滚动到下一个菜单元素。209. *

210. * @return 当前手势想滚动到下一个菜单元素返回true,否则返回false。

211. */

212.private boolean wantScrollToNext() {

213.return xUp - xDown < 0;

214. }

215.

216./**

217. * 判断是否应该滚动到下一个菜单元素。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,

218. * 就认为应该滚动到下一个菜单元素。

219. *

220. * @return 如果应该滚动到下一个菜单元素返回true,否则返回false。

221. */

222.private boolean shouldScrollToNext() {

223.return xDown - xUp > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;

224. }

225.

226./**

227. * 判断是否应该滚动到上一个菜单元素。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,

228. * 就认为应该滚动到上一个菜单元素。

229. *

230. * @return 如果应该滚动到上一个菜单元素返回true,否则返回false。

231. */

232.private boolean shouldScrollToPrevious() { 233.return xUp - xDown > switcherViewWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;

234. }

235.

236./**

237. * 刷新标签元素布局,每次currentItemIndex值改变的时候都应该进行刷新。

238. */

239.private void refreshDotsLayout() {

240. dotsLayout.removeAllViews();

241.for (int i = 0; i < itemsCount; i++) { 242. https://www.360docs.net/doc/d13996736.html,youtParams linearParams = new https://www.360docs.net/doc/d13996736.html,youtParams(0,

243. LayoutParams.FILL_PARENT); 244. linearParams.weight = 1;

245. RelativeLayout relativeLayout = new Relat iveLayout(getContext());

246. ImageView image = new ImageView(getContex t());

247.if (i == currentItemIndex) {

248. image.setBackgroundResource(R.drawabl

e.dot_selected);

249. } else {

250. image.setBackgroundResource(R.drawabl

e.dot_unselected);

251. }

252. https://www.360docs.net/doc/d13996736.html,youtParams relativeParam s = new https://www.360docs.net/doc/d13996736.html,youtParams(

253. LayoutParams.WRAP_CONTENT, Layout Params.WRAP_CONTENT);

254. relativeParams.addRule(RelativeLayout.CEN TER_IN_PARENT);

255. relativeLayout.addView(image, relativePar ams);

256. dotsLayout.addView(relativeLayout, linear Params);

257. }

258. }

259.

260./**

261. * 创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。

262. *

263. * @param event

264. * 右侧布局监听控件的滑动事件

265. */

266.private void createVelocityTracker(MotionEvent ev ent) {

267.if (mVelocityTracker == null) {

268. mVelocityTracker = VelocityTracker.obtain ();

269. }

270. mVelocityTracker.addMovement(event);

271. }

272.

273./**

274. * 获取手指在右侧布局的监听View上的滑动速度。

275. *

276. * @return 滑动速度,以每秒钟移动了多少像素值为单位。277. */

278.private int getScrollVelocity() {

279. https://www.360docs.net/doc/d13996736.html,puteCurrentVelocity(1000) ;

280.int velocity = (int) mVelocityTracker.getXVel ocity();

281.return Math.abs(velocity);

282. }

283.

284./**

285. * 回收VelocityTracker对象。

286. */

287.private void recycleVelocityTracker() {

288. mVelocityTracker.recycle();

289. mVelocityTracker = null;

290. }

291.

292./**

293. * 检测菜单滚动时,是否有穿越border,border的值都存储在{@link #borders}中。

294. *

295. * @param leftMargin

296. * 第一个元素的左偏移值

297. * @param speed

298. * 滚动的速度,正数说明向右滚动,负数说明向左滚动。

299. * @return 穿越任何一个border了返回true,否则返回false。

300. */

301.private boolean isCrossBorder(int leftMargin, int speed) {

302.for (int border : borders) {

303.if (speed > 0) {

304.if (leftMargin >= border && leftMargi n - speed < border) {

305.return true;

306. }

307. } else {

308.if (leftMargin <= border && leftMargi n - speed > border) {

309.return true;

310. }

311. }

312. }

313.return false;

314. }

315.

316./**

317. * 找到离当前的leftMargin最近的一个border值。318. *

319. * @param leftMargin

320. * 第一个元素的左偏移值

321. * @return 离当前的leftMargin最近的一个border值。322. */

323.private int findClosestBorder(int leftMargin) { 324.int absLeftMargin = Math.abs(leftMargin); 325.int closestBorder = borders[0];

326.int closestMargin = Math.abs(Math.abs(closest Border) - absLeftMargin);

327.for (int border : borders) {

328.int margin = Math.abs(Math.abs(border) - absLeftMargin);

329.if (margin < closestMargin) {

330. closestBorder = border;

331. closestMargin = margin;

332. }

333. }

334.return closestBorder;

335. }

336.

337.class ScrollTask extends AsyncTask {

338.

339.@Override

340.protected Integer doInBackground(Integer... s peed) {

341.int leftMargin = firstItemParams.leftMarg in;

342.// 根据传入的速度来滚动界面,当滚动穿越border 时,跳出循环。

343.while (true) {

344. leftMargin = leftMargin + speed[0]; 345.if (isCrossBorder(leftMargin, speed[0 ])) {

346. leftMargin = findClosestBorder(le ftMargin);

347.break;

348. }

349. publishProgress(leftMargin);

350.// 为了要有滚动效果产生,每次循环使线程睡眠10毫秒,这样肉眼才能够看到滚动动画。

351. sleep(10);

352. }

353.return leftMargin;

354. }

355.

356.@Override

357.protected void onProgressUpdate(Integer... le ftMargin) {

358. firstItemParams.leftMargin = leftMargin[0 ];

359. firstItem.setLayoutParams(firstItemParams );

360. }

361.

362.@Override

363.protected void onPostExecute(Integer leftMarg in) {

364. firstItemParams.leftMargin = leftMargin;

365. firstItem.setLayoutParams(firstItemParams );

366. }

367. }

368.

369./**

370. * 使当前线程睡眠指定的毫秒数。

371. *

372. * @param millis

373. * 指定当前线程睡眠多久,以毫秒为单位

374. */

375.private void sleep(long millis) {

376.try {

377. Thread.sleep(millis);

378. } catch (InterruptedException e) {

379. e.printStackTrace();

380. }

381. }

382.}

细心的朋友可以看出来,我还是重用了很多之前的代码,这里有几个重要点我说一下。在onLayout 方法里,重定义了各个包含图片的控件的大小,然后为每个包含图片的控件都注册了一个touch事件监听器。这样当我们滑动任何一样图片控件的时候,都会触发onTouch事件,然后通过改变第一个图片控件的leftMargin,去实现动画效果。之后在onLayout里又动态加入了页签View,有几个图片控件就会加入几个页签,然后根据currentItemIndex来决定高亮显示哪一个页签。其它也没什么要特别说明的了,更深的理解大家去看代码和注释吧。

然后看一下布局文件中如何使用我们自定义的这个控件,创建或打开activity_main.xml,里面加入如下代码:

1.

2.xmlns:tools="https://www.360docs.net/doc/d13996736.html,/tools"

3.android:layout_width="fill_parent"

4.android:layout_height="fill_parent"

5.android:orientation="horizontal"

6.tools:context=".MainActivity">

7.

8.

9.android:id="@+id/slidingLayout"

10.android:layout_width="fill_parent"

11.android:layout_height="100dip">

12.

13.

14.android:layout_width="fill_parent"

15.android:layout_height="fill_parent"

16.android:orientation="horizontal">

17.

18.

19.android:layout_width="fill_parent"

20.android:layout_height="fill_parent"

21.android:background="@drawable/image1"/>

22.

23.

24.android:layout_width="fill_parent"

25.android:layout_height="fill_parent"

26.android:background="@drawable/image2"/>

27.

28.

29.android:layout_width="fill_parent"

30.android:layout_height="fill_parent"

31.android:background="@drawable/image3"/>

32.

33.

34.android:layout_width="fill_parent"

35.android:layout_height="fill_parent"

36.android:background="@drawable/image4"/>

37.

38.

39.

40.android:layout_width="60dip"

41.android:layout_height="20dip"

42.android:layout_alignParentBottom="true"

43.android:layout_alignParentRight="true"

44.android:layout_margin="15dip"

45.android:orientation="horizontal">

46.

47.

48.

49.

我们可以看到,com.example.viewswitcher.SlidingSwitcherView的根目录下放置了两个LinearLayout。第一个LinearLayout中要放入需要滚动显示的图片,这里我们加入了四个Button,每个Button都设置了一张背景图片。第二个LinearLayout中不需要加入任何东西,只要控制好大小和位置,标签会在运行的时候自动加入到这个layout中。

然后创建或打开MainActivity作为主界面,里面没有加入任何新增的代码:

[java]view plaincopy

1.public class MainActivity extends Activity {

2.

3.@Override

4.protected void onCreate(Bundle savedInstanceState) {

5.super.onCreate(savedInstanceState);

6. setContentView(https://www.360docs.net/doc/d13996736.html,yout.activity_main);

7. }

8.

9.}

最后是给出AndroidManifest.xml的代码,也都是自动生成的内容:

[html]view plaincopy

1.

2.

3.package="com.example.viewswitcher"

4.android:versionCode="1"

5.android:versionName="1.0">

6.

7.

8.android:minSdkVersion="8"

9.android:targetSdkVersion="8"/>

10.

11.

12.android:allowBackup="true"

13.android:icon="@drawable/ic_launcher"

14.android:label="@string/app_name"

15.android:theme="@android:style/Theme.NoTitleBar"

>

16.

17.android:name="com.example.viewswitcher.MainA

ctivity"

18.android:label="@string/app_name">

19.

20.

21.

22.

23.

24.

25.

26.

27.

android 自定义圆角头像以及使用declare-styleable进行配置属性解析

android 自定义圆角头像以及使用declare-styleable进行配置属性解析由于最新项目中正在检查UI是否与效果图匹配,结果关于联系人模块给的默认图片是四角稍带弧度的圆角,而我们截取的图片是正方形的,现在要给应用统一替换。应用中既用到大圆角头像(即整个头像是圆的)又用到四角稍带弧度的圆角头像,封装一下以便重用。以下直接见代码 [java] view plain copy 在CODE上查看代码片派生到我的代码片 package com.test.demo; import com.test.demo.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.widget.ImageView; /** * 圆角imageview */ public class RoundImageView extends ImageView { private static final String TAG = "RoundImageView"; /** * 图片的类型,圆形or圆角 */ private int type; public static final int TYPE_CIRCLE = 0; public static final int TYPE_ROUND = 1; /** * 圆角大小的默认值

android studio 控件常用属性

android studio 控件常用属性 下面是RelativeLayout各个属性 1.android:layout_above="@id/xxx" --将控件置于给定ID控件之上 2.android:layout_below="@id/xxx" --将控件置于给定ID控件之下 3. android:layout_toLeftOf="@id/xxx" --将控件的右边缘和给定ID控件的左边缘对齐 4.android:layout_toRightOf="@id/xxx" --将控件的左边缘和给定ID控件的右边缘对齐 5. android:layout_alignLeft="@id/xxx" --将控件的左边缘和给定ID控件的左边缘对齐 6.android:layout_alignTop="@id/xxx" --将控件的上边缘和给定ID控件的上边缘对齐 7.android:layout_alignRight="@id/xxx" --将控件的右边缘和给定ID控件的右边缘对齐 8.android:layout_alignBottom="@id/xxx" --将控件的底边缘和给定ID控件的底边缘对齐 9.android:layout_alignParentLeft="true" --将控件的左边缘和父控件的左边缘对齐 10. android:layout_alignParentTop="true" --将控件的上边缘和父控件的上边缘对齐 11. android:layout_alignParentRight="true" --将控件的右边缘和父控件的右边缘对齐 12.android:layout_alignParentBottom="true" --将控件的底边缘和父控件的底边缘对齐 13.android:layout_centerInParent="true" --将控件置于父控件的中心位置 14.android:layout_centerHorizontal="true" --将控件置于水平方向的中心位置 15.android:layout_centerVertical="true" --将控件置于垂直方向的中心位置 android:layout_width 设置组件的宽度 android:layout_height 设置组件的高度 android:id 给组件定义一个id值,供后期使用 android:background 设置组件的背景颜色或背景图片 android:text 设置组件的显示文字 android:textColor 设置组件的显示文字的颜色 android:layout_below 组件在参考组件的下面 android:alignTop 同指定组件的顶平行

android毕业设计(论文)开题报告

毕业设计(论文) 开题报告 题目___________________________ 学院___________________________ 专业及班级___________________________ 姓名___________________________ 学号___________________________ 指导教师 ___________________________ 日期 ___________________________

西安科技大学毕业设计(论文)开题报告

二、主要研究(设计)内容、研究(设计)思路及工作方法或工作流程 设计内容:基于Android平台下实现理货员的功能:精耕拜访,销售机会,拜访效果,门店销量查询,待办事项,数据维护:队列信息,基础信息,地图下载。 设计思路及工作方法: 1.精耕拜访: (1)初始进入该页面,系统会自动获取该理货员的本日拜访的门店及路线。 (2)下方地图会自动定位目前理货员的位置。 (3)点击门店的具体门店名称,在右边会显示该门店的联系人,电话,及地址,并且地图 会自动切换定位以该门店为中心。 (4)点击地图上的+可实现地图局部放大。-可实现地图的放小。 (5)如已拜访的门店,点击进入可以调去历史拜访数据。如未拜访的门店,点击进入可以新增该拜访记录。 (6)如理货员在拜访期间,发现门店已经有卖而目前系统中无记录的情况,则可以通过扫 描SKU的条形码,系统会自动匹配该SKU的信息给理货员。 匹配顺序:连锁总店—区域管理—基本SKU信息 补充说明: (1)公司动态内容,为了显示保乐力加集团的整体形象,有必要对公司的动态进行展示。 要包括集团的光辉历程,门店信息,集团的促销信息等。 (2)在陈列信息栏中,有其它途径过来的回应确认,可以一栏展示在上半部。如该理货员 需要发起确认,也可以通过点击发起确认进行提出。发起确认的需要销售员进行审核并且 分发。 (3)陈列报告,为显示每次理货员必须处理的门店信息记录及图片等。 (4)在陈列信息栏中,有其它途径过来的回应确认,可以一栏展示在上半部。如该理货员需要发起确认,也可以通过点击发起确认进行提出。发起确认的需要销售员进行审核并且分发。 (5)价格显示,系统会自动带出竞品信息。罗列在下半部。理货员在进行拜访期间,可以对本身的SKU及竞品的价格进行登记。主要登记内容:零售价格,促销价格及促销期间。信息采集完毕,可以供后台进行统计分析。 (7)在陈列信息栏中,有其它途径过来的回应确认,可以一栏展示在上半部。如该理货员需要发起确认,也可以通过点击发起确认进行提出。发起确认的需要销售员进行审核并且分发。 (7)库存信息,主要记录SKU的库存数量。 (8)上半部为助销申请内容,主要是对一些礼品的申请。 (9)订单处理,为新增订单,退货内容及换货内容的跟踪。 2.销售机会: (1)打开销售机会界面,会自动定位目前的门店列表。 (2)用户可以输入门店名称查询具体的门店信息及地图位置。

Android平台我的日记设计文档

Android平台我的日记 设计文档 项目名称:mydiray 项目结构示意: 阶段任务名称(一)布局的设计 开始时间: 结束时间: 设计者: 梁凌旭 一、本次任务完成的功能 1、各控件的显示 二、最终功能及效果 三、涉及知识点介绍 四、代码设计 activity_main.xml:

android:layout_centerHorizontal="true" android:layout_marginTop="88dp" android:text="@string/wo" android:textSize="35sp"/>

相关文档
最新文档