Ubuntu 12.04默认是不允许root登录的,在登录窗口只能看到普通用户和访客登录。以普通身份登陆Ubuntu后我们需要做一些修改,普通用户登录后,修改系统配置文件需要切换到超级用户模式,在终端窗口里面输入: sudo -s.然后输入普通用户登陆的密码,回车即可进入 root用户权限模式。
然后执行: vi /etc/lightdm/lightdm.conf.
增加 greeter-show-manual-login=true allow-guest=false . 修改完的整个配置文件是
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
greeter-show-manual-login=true #手工输入登陆系统的用户名和密码
allow-guest=false #不允许guest登录
然后我们启动root帐号:
sudo passwd root
根据提示输入roott帐号密码。
重启ubuntu,登录窗口会有“登录”选项,这时候我们就可以通过root登录了。
XNA框架认识
XNA是微软开发的一款用于window,xbox,windows phone等设备上的游戏开发框架,他的特点是方便快捷的开发游戏,提供给我们很多的游戏开发套件,摒弃了传统的游戏引擎多文件多目录的繁琐,使我们用起来容易上手,在wp8 SDK发布之后,目前微软已经表示wp8不再支持用XNA开发游戏,但是会完全兼容XNA游戏,所以XNA游戏还是可以在这些平台上运行(这点感觉微软有点坑爹)!
学习要求:XNA游戏开发主要是采用C#语言,但是要求不是很高,只需要我们掌握基础的语法 加上一定的利用API开发的经验就可以开始学习了!本博文系列的教材是《XNA4.0入门指南》,算是总结性质的吧,结合教材看效果更佳!
我的系统环境是windows 8,开发IDE用的是windows phone7.1 SDK,该开发包里面已经集成了XNA4.0的开发套件,也集成了简易的开发环境VS2010 express for windows phone!所以没有必要再VS2010的基础上安装7.1SDK,对于开发环境的搭建网上已有很多教程,在这里我就不累述了.这里附上windows phone 7.1SDK的官方下载地址 windows phone 7.1 SDK 接下来开始我们的XNA游戏开发之旅:
1:第一个XNA程序
工程创建:新建项目—XNA game studio 4.0—windows游戏 完成之后工作区如图所示
在这几个目录中,我们暂时只需要关注两个目录,一个是Programs.cs和Game1.cs,前面一个是主函数文件,他是游戏程序的入口点,说实话我们暂时不需要用他,后者才说我们的重点,点开目录,看到的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace WindowsGame5
{
/// <summary>
/// 这是游戏的主类型
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// 允许游戏在开始运行之前执行其所需的任何初始化。
/// 游戏能够在此时查询任何所需服务并加载任何非图形
/// 相关的内容。调用 base.Initialize 将枚举所有组件
/// 并对其进行初始化。
/// </summary>
protected override void Initialize()
{
// TODO: 在此处添加初始化逻辑
base.Initialize();
}
/// <summary>
/// 对于每个游戏会调用一次 LoadContent,
/// 用于加载所有内容。
/// </summary>
protected override void LoadContent()
{
// 创建新的 SpriteBatch,可将其用于绘制纹理。
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: 在此处使用 this.Content 加载游戏内容
}
/// <summary>
/// 对于每个游戏会调用一次 UnloadContent,
/// 用于取消加载所有内容。
/// </summary>
protected override void UnloadContent()
{
// TODO: 在此处取消加载任何非 ContentManager 内容
}
/// <summary>
/// 允许游戏运行逻辑,例如更新全部内容、
/// 检查冲突、收集输入信息以及播放音频。
/// </summary>
/// <param name="gameTime">提供计时值的快照。</param>
protected override void Update(GameTime gameTime)
{
// 允许游戏退出
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit(); // TODO: 在此处添加更新逻辑
base.Update(gameTime);
}
/// <summary>
/// 当游戏该进行自我绘制时调用此项。
/// </summary>
/// <param name="gameTime">提供计时值的快照。</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: 在此处添加绘图代码
base.Draw(gameTime);
}
}
}
在这段代码中您会注意到自动提供了几个类成员变量,还有Game1的构造方法和其它五个 方法。其中第 1 个类成员变量是GraphicsDeviceManager类的对象。这是一个非常重要的 对象,它为开发者提供了一种访问 PC、Xbox 360 或者 Windows Phone 7 上的图形设备的途径。 GraphicsDeviceManager的GraphicDevice属性代表了机器上实际的图形设备。图形设 备对象在 XNA 游戏和显卡(或更准确的说,显卡上的 GPU10)之间起中介作用,XNA 游戏在屏 幕上做的任何事情都要通过这个对象来执行!
第 2 个类成员变量是SpriteBatch类的实例,这是一个您将用来绘制精灵(Sprite)的核 心对象。
Initialize 方法被用来初始化变量和其它与 Game1 对象相关的对象。
LoadContent 方法在 Initialize 方法之后被调用,另外在任何需要重新载入游戏图形 内容的时候也会被调用, LoadContent 方法中,将会载入游戏所需要的一切图形及其它内容,包括图像、模型和音效等。当然UnLoadContent方法就是用来清理一些资源的!
当 LoadContent 方法调用完成后,Game1 对象将进入游戏循环。。在 XNA 中,游戏循环只包含两个方法:Update 方法和 Draw 方法。目前您可以这样理解游戏循 环:所有影响游戏的逻辑都将在 Update 方法或 Draw 方法中完成。其中drew用来绘制图形 update用来更新图形!
以下便是XNA程序执行的顺序:
我们先运行一下该项目看看什么效果:
我们可以看到屏幕就是一个窗口,什么都没有,而且屏幕背景颜色的蓝色的,顾名思义,这个蓝色肯定跟我们的draw方法脱不了干系,我们回过头看看draw的代码,发现这样一句:
GraphicsDevice.Clear(Color.CornflowerBlue);
他表示利用了当前的图形设备对象的一个成员函数clear,并且在参数中使用了蓝色,如果在这里把蓝色改成黑色,背景就自然的变成黑色了
2:在项目中添加一个精灵
在这里可以自己找一张图片,在资源管理器中的WindowsGameContent中新建一个文件夹Images,然后在我们创建的文件夹里面添加一张图片,XNA支持的格式多样,常见的都可以,在Images目录下会有一个图片文件,但是没有扩展名,这是XNA默认的去掉扩展名 作为该图片的ID,下面就可以添加到代码中了:
1:在 Game1.cs 代码文件中的 GraphicDevice Manager 和 SpriteBatch 的变量声明下面添加一个 Texture2D 变量: Texture2D texture;
其中Texture2D是一个存储2D文件的对象
2:就像之前说的那样,所有图像、声音和其它内容资源的加载都在 LoadContent 方法中完 成。添加以下代码到 LoadContent 方法中:
texture = Content.Load<Texture2D>(@"Images/logo");
这里也可以这样写,效果是一样的:
texture = Content.Load<Texture2D>("Images//logo"); 就是指初始化texture变量!接下来就可以直接使用texture变量表示这张图片了!
3:XNA 中所有的绘制工作都要在 Draw 方法中完成,因此请添加以下三行代码到 Draw 方法中,位于 Clear 方法的调用之后:
spriteBatch.Begin();
spriteBatch.Draw(texture, Vector2.Zero, Color.White);
spriteBatch.End();
相信有过win32 SDK编程经验的都熟悉这种写法,在win32 SDK编程中对于WM_PAINT消息响应都是一个begin 一个end 之间写出需要响应的窗口重绘代码,这里的spriteBatch
是在您创建项目的时候被声明,然后在 LoadContent 方法中被 初始化。 这里发生的事情就是 XNA 用 SpriteBatch 对象的 Begin 和 End 方法通知图形 设备将要发送一个精灵(或 2D 图像),这个可以死记!
第二行的Draw方法中第一个是需要绘制的2D对象,第二个是绘制的位置,这里Vector2是一个2D矢量类,zero是她的一个成员,默认为0,也就在左上角(0,0),第三个参数表示是否需要用颜色覆盖2D对象,这里的White表示不覆盖!
好了,运行一下程序:
在这里我把clear的颜色变为了黑色,然后用PS把图片编辑为透明背景的,所以出现这个结果!运行就看到了XNA的logo,挺漂亮的!
居中问题:
如果需要把图片居中,我们只需要改变draw第二个参数,该参数表示显示位置,指的是图片的左上角顶点的位置,这里需要利用两个新的成员,
spriteBatch.Draw(
texture,
new Vector2(Window.ClientBounds.Width/2 - texture.Width/2,
Window.ClientBounds.Height/2 - texture.Height/2),
Color.White
);
这里把第二个参数改为了自定义的坐标,其中windows.ClientBounds表示屏幕窗口的尺寸类,利用它的width和height就可以得到屏幕的长和宽,Texture2D对象内部本身也有标示2D对象的长和宽,利用这些成员,我们就可以把图片显示在屏幕中间!
3:关于精灵的一些属性变换(旋转,多张重叠,透明度,缩放)
上面在draw函数中利用了spriteBatch的成员函数draw的一个相对简单的版本,实际上他有很多重载版本,下面我们看看他的另一个版本:
Texture Texture2D 要绘制的纹理.
Position Vector2 绘制图像的左上角坐标.
SourceRectangle Rectangle 允许您绘制原始图像的一部分,这里使用 null.
Color Color 染色颜色. Rotation float 旋转图像,现在使用 0.
Origin Vector2 指定旋转的参照点.现在使用 Vector2.Zero.
Scale float 缩放比例,使用 1 代表按照原始尺寸绘制,1.5f 表示放大图像到 150%.
Effects SpriteEffects 使用 SpriteEffects 枚举来垂直或水平翻转图 像.
LayerDepth float 允许您指定图像的层叠次序(哪张图像在其它图 像之上).现在使用 0. 。0 相当于 Z 次序的前面,1 表示 Z 次序的后面。
可以看到他们的参数是很多的,对于透明度我们都是用PS来加工,所以一般不使用,缩放扩大也很简单,我主要说一下多张图片在一起重叠的时候是什么个情况!
按照同样的方法再多加一张图片到我们的项目,并用texture1来存储,经过LoadContent函数之后,draw函数里面的代码如下所示:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.BackToFront,BlendState.AlphaBlend);
spriteBatch.Draw(texture,
new Vector2(Window.ClientBounds.Width / 2 - texture.Width / 2,
Window.ClientBounds.Height / 2 - texture.Height / 2),
null,
Color.White,
0,
Vector2.Zero,
1.5f,
SpriteEffects.None,
0);
spriteBatch.Draw(texture1,
new Vector2(Window.ClientBounds.Width / 2 - texture1.Width / 2,
Window.ClientBounds.Height / 2 - texture1.Height / 2),
null,
Color.White,
0,
Vector2.Zero,
1f,
SpriteEffects.None,
1);
spriteBatch.End();
// TODO: 在此处添加绘图代码
base.Draw(gameTime);
}
}
}我们把透明背景的那张扩大为1.5倍,这样方便看出效果,把最后一个参数设置为0和1,一张在前面,一张被覆盖在后面,这里我们透明在前,不透明在后,运行结果:
可以看到运行结果并不是我们想的那样,为什么会这样勒?结果是先绘制的被后绘制的覆盖,显然不合我们的程序,经过查看Z次序详细说明,我们知道了要使用Z次序排列图片的重合问题,需要在begin函数中说明需要使用Z次序!
修改程序代码begin:
spriteBatch.Begin( SpriteSortMode.FrontToBack, BlendSate.AlphaBlend);
这里的第一个参数表示Z次序的状态,另一个参数在使用透明图片的时候这样来设置,
再次编译运行:
结果运行就符合我们最初的想法了!(注:由于篇幅有限,这里很多东西参考MSDN)
4:开始简单的移动精灵
前面我们介绍过游戏画面的移动都是在循环里执行的,这里的循环不需要我们自己写,因为她提供给了我们draw和update函数,draw函数参数里面传递了一个gameTime参数,他实际表示帧率,即每一秒重绘多少次,系统默认的是60次,也就是说1秒钟内调用了60次draw函数,每次调用draw函数之后又会调用update函数,一共调用了120次函数来不停的刷新游戏画面,所以我们只需要在update里面加入移动的代码,精灵就会移动起来!
为了移动精灵,我们需要再加入两个Vector2变量来指示当前位置,并用两个float变量表示每一帧的移动速度,整体代码如下(其实也只有初始化,draw和update函数有一定的变化)
namespace WindowsGame4
{
/// <summary>
/// 这是游戏的主类型
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D texture;
Texture2D texture1;
Vector2 pos1 = Vector2.Zero; //表示图片的初始位置
Vector2 pos2 = Vector2.Zero;
float speed1 = 2f; //表示图片每一帧的移动速度
float speed2 = 3f;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: 在此处添加初始化逻辑
base.Initialize();
}
protected override void LoadContent()
{
// 创建新的 SpriteBatch,可将其用于绘制纹理。
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load<Texture2D>("image//logo_trans");
texture1 = Content.Load<Texture2D>("image//logo");
// TODO: 在此处使用 this.Content 加载游戏内容
}
protected override void UnloadContent()
{
// TODO: 在此处取消加载任何非 ContentManager 内容
}
protected override void Update(GameTime gameTime)
{
// 允许游戏退出
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
pos1.X += speed1;
if(pos1.X == Window.ClientBounds.Width-texture.Width || pos1.X < 0)
{
speed1 *= -1;
}
pos2.Y += speed2;
if (pos2.Y == Window.ClientBounds.Height - texture1.Height || pos2.Y < 0)
speed2 *= -1;
// TODO: 在此处添加更新逻辑
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.BackToFront,BlendState.AlphaBlend);
spriteBatch.Draw(texture,
pos1,
null,
Color.White,
0,
Vector2.Zero,
1f,
SpriteEffects.None,
0);
spriteBatch.Draw(texture1,
pos2,
null,
Color.White,
0,
Vector2.Zero,
1f,
SpriteEffects.None,
1);
spriteBatch.End();
// TODO: 在此处添加绘图代码
base.Draw(gameTime);
}
}
}
那么我们运行就可以看到图片在上下移动:
这里没什么复杂的东西,只是多了绘制更新的代码而已 如果需要图片更加复杂的运动,原理也是一样,只是多了复杂的数学运算!!
5:简单的动画效果
也许大家都知道,动画的形成也只是不停的绘制精灵,使他们形成连续播放的效果,在这里我们插入这样一张图片:
可以看到这张图片的每个小位图,连在一起是不是很像动画了,我们现在要做的就是分别把他们显示出来,在这里我们新建一个项目,采取同样的方法加载进这张图片,并用texture变量保存!(同上,不在演示了)
我们在上面的程序中都是讲整张图片显示出来,在这里我们又需要用draw函数的一个参数了,上面已经给出了参数列表,其中有一个我们一直用null,表示全部显示出来,这里我们就需要用到它了!
为了编写这个算法,我们还需要知道这些:
• 精灵位图中每个单独图像(或称为帧)的宽和高(frameSize)。
• 精灵位图的行与列的总数(sheetSize)。
• 指示接下来精灵位图中将要绘制的精灵帧在精灵位图中所处的行与列的位置的索引 (currentFrame)。
在我们这里,每个位图的长和宽都是75像素,有8行6列,接下里我们用一个矩形来代替上面的null,但是一个矩形需要知道它的左顶点以及长和宽,为此我们定义如下变量:
Point frameSize = new Point(75, 75); Point currentFrame = new Point(0, 0); Point sheetSize = new Point(6, 8);
之所以利用点集,是因为我们这里只是需要成对的存储元素,所以point足够了!
下面请看全部函数的代码:
namespace WindowsGame6
{
/// <summary>
/// 这是游戏的主类型
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D texture;
Point frameSize = new Point(75, 75); //表示每个位图的长和宽
Point currentFrame = new Point(0, 0); //表示当前该轮到哪个位图绘制
Point sheetSize = new Point(6, 8); //指示了最大的长和宽
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: 在此处添加初始化逻辑
base.Initialize();
}
protected override void LoadContent()
{
// 创建新的 SpriteBatch,可将其用于绘制纹理。
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load<Texture2D>("Images//threerings"); //加载位图资源
// TODO: 在此处使用 this.Content 加载游戏内容
}
protected override void UnloadContent()
{
// TODO: 在此处取消加载任何非 ContentManager 内容
}
protected override void Update(GameTime gameTime)
{
// 允许游戏退出
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
++currentFrame.X; //这里执行更新 看不清楚可以一步一步分析
if (currentFrame.X >= sheetSize.X)
{
currentFrame.X = 0;
++currentFrame.Y;
if (currentFrame.Y >= sheetSize.Y)
currentFrame.Y = 0;
}
// TODO: 在此处添加更新逻辑
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
spriteBatch.Draw(
texture,Vector2.Zero,
new Rectangle(currentFrame.X * frameSize.X, //这里新创一个矩形
currentFrame.Y * frameSize.Y,
frameSize.X, frameSize.Y),
Color.White,
0,
Vector2.Zero,
1,
SpriteEffects.None,
0);
spriteBatch.End();
// TODO: 在此处添加绘图代码
base.Draw(gameTime);
}
}
}
运行就可以看到三个圈在转动:
这样我们的简单动画就完成了!
最近在研究path的首页滑动效果,发现啪啪也已经实现了这个效果,自己网上找了代码,又自己试试,发现如果是用两个ImageView 做的话,可以实现,但是用listView的话,滑动就会出问题,现在把代码粘贴如下,望各位指点迷津:
主界面的xml:
<com.example.testpathscroll.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv1"
android:layout_width="fill_parent"
android:layout_height="200dip"
android:scaleType="center"
android:src="/blog_article/@drawable/bg/index.html" />
<ImageView
android:id="@+id/iv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:src="/blog_article/@drawable/abcd/index.html" />
</LinearLayout>
<RelativeLayout
android:id="@+id/clock_rl"
android:layout_width="wrap_content"
android:layout_height="158dp" >
<include
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_marginTop="40dp"
layout="@layout/clock" />
</RelativeLayout>
</FrameLayout>
</com.example.testpathscroll.MyScrollView>
其中主要代码就是第一个Linear中的两个ImageView;
自定义的MyScrollView:
public class MyScrollView extends ScrollView {
public static interface OnPositionChangedListener {
public void onPositionChanged(int position);
public void onScollPositionChanged(View scrollBarPanel, int top);
}
private OnPositionChangedListener mPositionChangedListener;
static int k = 50;
ImageView iv1;
ImageView iv2;
int left, top;
// 记录初始的位置
int m_top;
float startX, startY;
float currentX, currentY;
int rootW, rootH;
private boolean isCount = false;// 是否开始计算
int iv1H;
int iv2H;
// 记录下面布局的上高度,就是上面图片的下边
int t;
Scroller scroller;
int myHeight, myHeight2;
int myWidth, myWidth2;
// View mClock;
Context mContext;
// 时间
// PopupWindow mPopUp;
// 判断显示还是隐藏
boolean flag = false;
ScrollView mScrollView;
RelativeLayout mRelativel;
int temp;
TranslateAnimation ta=null;
//尝试使用Scroller
Scroller mScroller;
/*sdfsdfsdf*/
private View inner1,inner2;// 孩子
private float y;// 坐标
private Rect normal2 = new Rect();// 矩形空白
private Rect normal1 = new Rect();// 矩形空白
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// 第一张图片的高度,这个是需要(可以按照比例来制定,或者使用固定值,按照需要来)
Bitmap bm = BitmapCache.getInstance().getBitmap(R.drawable.bg, context);
myHeight = bm.getHeight();
myWidth = bm.getWidth();
// 第二张图片
Bitmap bm2 = BitmapCache.getInstance().getBitmap(R.drawable.abcd,
context);
myHeight2 = bm2.getHeight();
myWidth2 = bm2.getWidth();
temp=(int) (200*context.getResources().getDisplayMetrics().density);
//
mScroller=new Scroller(context);
// setFocusable(true);
// setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
// setWillNotDraw(false);
}
public void setOnPositionChangedListener(
OnPositionChangedListener onPositionChangedListener) {
mPositionChangedListener = onPositionChangedListener;
}
// public void openTimeShow() {
// // set position
// mPopUp.showAtLocation(findViewById(R.id.ll), Gravity.CENTER, 0, 0);
// // mPopUp.showAsDropDown(view, 0, 0);
// }
// public void closeTimeShow(){
// mPopUp.dismiss();
// }
/**
* 所有子View被加载后触发
* */
protected void onFinishInflate() {
super.onFinishInflate();
iv1 = (ImageView) findViewById(R.id.iv1);
iv2 = (ImageView) findViewById(R.id.iv);
// mClock=(View) findViewById(R.id.clock);
mScrollView = (ScrollView) findViewById(R.id.ll);
setLongClickable(true);
scroller = new Scroller(getContext(),
new AccelerateDecelerateInterpolator());
// View view=LayoutInflater.from(mContext).inflate(R.id.clock_rl, null);
// mPopUp=new PopupWindow(view, LayoutParams.WRAP_CONTENT,
// LayoutParams.WRAP_CONTENT);
// mPopUp.setFocusable(false);
// mPopUp.setOutsideTouchable(true);
mRelativel = (RelativeLayout) findViewById(R.id.clock_rl);
/*sdfsdf*/
inner1 = iv1;
inner2 = iv2;// 获取其孩子
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
super.onLayout(changed, l, t, r, b);
m_top = iv2.getTop();
// 两张图片(两个布局的高度)
iv1H = iv1.getHeight();
iv2H = iv2.getHeight();
Log.i("TAG", iv1H + "---" + iv2H + "---" + m_top);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
currentX = event.getX();
currentY = event.getY();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
if(ta!=null){
ta.cancel();
ta=null;
}
left = iv2.getLeft();
top = iv2.getTop();
rootW = getWidth();
rootH = getHeight();
// Log.i("TAG", "iv1H高度ACTION_DOWN---"+iv1H);
currentX = event.getX();
currentY = event.getY();
startX = currentX;
startY = currentY;
//Rect2
if (normal2.isEmpty()) {
// 填充矩形,目的:就是告诉this:我现在已经有了,你松开的时候记得要执行回归动画.
normal2.set(inner2.getLeft(), inner2.getTop(),
inner2.getRight(), inner2.getBottom());
}
//Rect1
if (normal1.isEmpty()) {
// 填充矩形,目的:就是告诉this:我现在已经有了,你松开的时候记得要执行回归动画.
normal1.set(inner1.getLeft(), inner1.getTop(),
inner1.getRight(), inner1.getBottom());
}
mPositionChangedListener.onScollPositionChanged(this,
getHeight() / 2);
break;
}
case MotionEvent.ACTION_MOVE:
Log.i("TAG", iv2.getY()+"!!!!!!!!!!!!!!!!!!");
int c_current = (int) currentY;
int l = (int) (left + currentX - startX);
int deltaY=(int) (currentY - startY);
if (!isCount) {
deltaY = 0; // 在这里要归0.
}
// 获取滑动距离
t = (int) (top + deltaY);
// Log.i("TAG", (currentY -
// startY)+"-----currentY - startY----!!!!"+currentY+"!!!!"+startY);
if (deltaY >= 0) {// 向下滑动
if (t > myHeight) {
t = myHeight;
}
// 控制时间条的位置
if (c_current > 854) {
c_current = 854;
}
} else {// 向上滑动
// 854 IS A TEST PX,the Height of screen
if (myHeight2 >= 854) {
if ((myHeight2 - 854) < -t) {
t = 854 - myHeight2;
}
} else {
if (myHeight2 - t < 800) {
t = 854 - myHeight2;
}
}
// 控制位置(800是屏幕的高度)
if (c_current < 0) {
c_current = 0;
}
}
mPositionChangedListener.onPositionChanged((int) currentY);
mPositionChangedListener.onScollPositionChanged(this,
(int) (startY - currentY));
// iv2.layout(left, t, left + iv2.getWidth(), t + iv2.getHeight());
inner2.layout(left, t, left + iv2.getWidth(), t + iv2.getHeight());
inner1.layout(0, 0, iv1.getWidth(), t);
isCount = true;
break;
case MotionEvent.ACTION_UP:
// closeTimeShow();
if (t <= temp) {// m_top
} else {
// System.out.println(temp+"------------"+t);
// animation(iv2, 0, 0, 0, temp - t);
// animation(iv1, 0, 0, 0, -temp/2);
if (isNeedAnimation()) {
animation();
isCount = false;
}
// 更新结束后,使用动画控制偏移过程, 3s内到位
// //mScroller.startScroll(0, 0, 0, temp,3000);
// // 重绘控件
// invalidate();
// iv2.layout(left, temp, left + iv2.getWidth(), temp + iv2.getHeight());
// iv1.layout(0, 0, iv1.getWidth(), temp);
}
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
}
return true;
}
/***
* 是否需要开启动画
*
* 如果矩形不为空,返回true,否则返回false.
*
*
* @return
*/
public boolean isNeedAnimation() {
return !normal2.isEmpty()&&!normal1.isEmpty();
}
/***
* 开启动画移动
*/
public void animation() {
// 开启移动动画
System.out.println(inner2.getTop()+"----inner2.getTop()----"+normal2.top+"---normal.top-----");
TranslateAnimation ta = new TranslateAnimation(0, 0, inner2.getTop()-normal2.top,
0);
ta.setDuration(300);
// TranslateAnimation ta1=new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 1f, Animation.RELATIVE_TO_PARENT, 1f, Animation.RELATIVE_TO_PARENT, 1f, Animation.RELATIVE_TO_PARENT, 0.5f);
// ta1.setDuration(500);
// ScaleAnimation sa=new ScaleAnimation(1.0f, 1.0f, 1.5f, 1.0f,
// Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f);
// sa.setDuration(300);
MyAnimation ma=new MyAnimation();
inner2.startAnimation(ta);
inner1.startAnimation(ma);
// 设置回到正常的布局位置
inner2.layout(normal2.left, normal2.top, normal2.right, normal2.bottom);
inner1.layout(normal1.left, normal1.top, normal1.right, normal1.bottom);
// 清空矩形
normal1.setEmpty();
normal2.setEmpty();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) { // 如果返回true,表示动画还没有结束
// 产生平滑的动画效果,根据当前偏移量,每次滚动一点
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
// 此时同样也需要刷新View ,否则效果可能有误差
postInvalidate();
} else { //如果返回false,表示startScroll完成
}
}
public void animation(View view, float fromx, float tox, float fromY,
float toy) {
// 开启移动动画
ta = new TranslateAnimation(0, 0, fromY, toy);
ta.setInterpolator(new AccelerateDecelerateInterpolator());
ta.setDuration(500);
ta.setFillAfter(true);
// if(view.getId()==iv2.getId()){
ta.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//返回初始位置
iv2.layout(left, temp, left + iv2.getWidth(), temp + iv2.getHeight());
iv1.layout(0, 0, iv1.getWidth(), temp);
// view.clearAnimation();
// postInvalidate();
// Log.i("TAG", "iv1.getY() + iv2.getY()"+iv1.getY()+"----" + iv2.getY()+"---"+temp);
}
});
// }
view.startAnimation(ta);
// 设置回到正常的布局位置
}
}
这段代码就是处理下拉,反弹效果的。
主界面java文件:
public class MainActivity extends Activity implements OnPositionChangedListener{// implements OnTouchListener, OnPositionChangedListener
MyScrollView ll;
FrameLayout clockLayout;
private ExtendedListView dataListView;
private boolean areButtonsShowing;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ll = (MyScrollView) findViewById(R.id.ll);
clockLayout = (FrameLayout)findViewById(R.id.clock);
ll.setOnPositionChangedListener(this);
}
private float[] computMinAndHour(int currentMinute, int currentHour) {
float minuteRadian = 6f * currentMinute;
float hourRadian = 360f / 12f * currentHour;
float[] rtn = new float[2];
rtn[0] = minuteRadian;
rtn[1] = hourRadian;
return rtn;
}
private float[] lastTime = {
0f, 0f
};
private RotateAnimation[] computeAni(int min, int hour) {
RotateAnimation[] rtnAni = new RotateAnimation[2];
float[] timef = computMinAndHour(min, hour);
// AnimationSet as = new AnimationSet(true);
// 创建RotateAnimation对象
// 0--图片从哪开始旋转
// 360--图片旋转多少度
// Animation.RELATIVE_TO_PARENT, 0f,// 定义图片旋转X轴的类型和坐标
// Animation.RELATIVE_TO_PARENT, 0f);// 定义图片旋转Y轴的类型和坐标
RotateAnimation ra = new RotateAnimation(lastTime[0], timef[0], Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
ra.setFillAfter(true);
ra.setFillBefore(true);
// 设置动画的执行时间
ra.setDuration(800);
// 将RotateAnimation对象添加到AnimationSet
// as.addAnimation(ra);
// 将动画使用到ImageView
rtnAni[0] = ra;
lastTime[0] = timef[0];
// AnimationSet as2 = new AnimationSet(true);
// 创建RotateAnimation对象
// 0--图片从哪开始旋转
// 360--图片旋转多少度
// Animation.RELATIVE_TO_PARENT, 0f,// 定义图片旋转X轴的类型和坐标
// Animation.RELATIVE_TO_PARENT, 0f);// 定义图片旋转Y轴的类型和坐标
RotateAnimation ra2 = new RotateAnimation(lastTime[1], timef[1], Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
// 设置动画的执行时间
ra2.setFillAfter(true);
ra2.setFillBefore(true);
ra2.setDuration(800);
// 将RotateAnimation对象添加到AnimationSet
// as2.addAnimation(ra2);
// 将动画使用到ImageView
rtnAni[1] = ra2;
lastTime[1] = timef[1];
return rtnAni;
}
@Override
public void onPositionChanged(int position) {
TextView datestr = ((TextView) findViewById(R.id.clock_digital_date));
datestr.setText("上午");
int hour = Calendar.getInstance().getTime().getHours()+position;
String tmpstr = "";
if (hour > 12) {
hour = hour - 12;
datestr.setText("下午");
tmpstr += " ";
} else if (0 < hour && hour < 10) {
tmpstr += " ";
}
tmpstr += hour + ":" + Calendar.getInstance().getTime().getMinutes();
((TextView) findViewById(R.id.clock_digital_time)).setText(tmpstr);
RotateAnimation[] tmp = computeAni(Calendar.getInstance().getTime().getMinutes(),hour);
ImageView minView = (ImageView) findViewById(R.id.clock_face_minute);
minView.startAnimation(tmp[0]);
ImageView hourView = (ImageView) findViewById(R.id.clock_face_hour);
hourView.setImageResource(R.drawable.clock_hour_rotatable);
hourView.startAnimation(tmp[1]);
}
@Override
public void onScollPositionChanged(View scrollBarPanel, int top) {
MarginLayoutParams layoutParams = (MarginLayoutParams)clockLayout.getLayoutParams();
layoutParams.setMargins(0, top, 0, 0);
clockLayout.setLayoutParams(layoutParams);
}
}
上面的效果经过测试还可以达到要求。但是实际项目中用到的可能是listview,而不是ImageView,这就会产生点击事件传递的问题。
希望有经验的大侠给点指点~小弟不胜感激!
文件下载地址:http://download.csdn.net/detail/ansionnal/5213545